Hungry Mind , Blog about everything in IT - C#, Java, C++, .NET, Windows, WinAPI, ...

Easy reporting to Word, PDF, HTML

Тяжело работать с людьми, которые сами не знают, чего хотят. Еще хуже, если эти люди являются заказчиками программного обеспечения. Они требуют за копейки кнопку "Сделать все". А потом еще чуть-чуть. И я уже не первый раз наблюдаю эту ситуацию. А самое веселое, когда дело доходит до отчетов. Мы хотим, чтобы был дизайнер. А еще мы хотим, чтобы все было попроще. А еще мы не знаем, что это будут за отчеты, так что сделайте универсальное решение. И ломай потом голову.

Я считаю, что дизайнер стоит кучу денег, для его написания нужен не один человеко-год. И пока что не видел ни одного универсального решения, с которым пользователь ощущал бы себя не обезьяной. Наиболее приемлемый вариант - вбивать логику построения отчета в программу. Дешево и сердито! Ну, дальше изложу один из вариантов такого решения.

Для получения отчета нужно:

  1. Данные

    Строки, числа, даты необходимые для построения отчета.

  2. Шаблон

    Набор правил построения отчета.

  3. Процессор

    Механизм, преобразующий данные по правилам.

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

XML, XSLT 2.0 и Altova StyleVision

Итак, данные в формате XML преобразуются XSLT процессором в выходной файл.

Altova StyleVision является нашим самым главным фигурантом и помощником, он же developer-friendly designer. Это визуальный редактор, который позволяет получить на выходе XSLT 2.0 файл, преобразующий XML в HTML, RTF или PDF!

Я уже точно не помню, зачем понадобился именно XSLT 2.0, вероятно из-за каких-то фич дизайнера, но вот найти бесплатный процессор для Java было напряжно. Из коммерческих есть Oracle XML Developer's Kit, из некоммерческих - Saxon. Других не нашел! С .NET ситуация хуже, под него только Saxon. Ну, Saxon так Saxon!

Исходные данные

Исходные данные получаем из источника данных (БД, например) и формируем XML файл с определенной схемой, которая описывается в XSD. Механизм формирования не имеет значения, но, если данные качаются из БД, я бы предпочел возложить этот процесс на сервер (далее в примере я буду получать от любимой СУБД Oracle сразу XML, а если поднатужиться, то и файл с отчетом можно получить!).

Я буду использовать существующую схему SH (Sales History). Как ее установить написано здесь.

Итак, наш отчет будет выдавать некую информацию о клиенте - имя, фамилия, пол, дата рождения и т.д. StyleVision хочет видеть XSD схему документов, которые прийдется использовать в качестве данных. Схему можно создавать либо вручную, подгоняя потом под нее XML, либо построить по существующему XML. Все это, кстати умеет делать Altova XMLSpy. Вот моя схема (Customer.xsd):

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <xs:simpleType name="T_Valid">
      <xs:restriction base="xs:string">
         <xs:enumeration value="I"/>
         <xs:enumeration value="A"/>
      </xs:restriction>
   </xs:simpleType>
   <xs:simpleType name="T_Gender">
      <xs:restriction base="xs:string">
         <xs:enumeration value="M"/>
         <xs:enumeration value="F"/>
      </xs:restriction>
   </xs:simpleType>
   <xs:complexType name="T_Customer">
      <xs:sequence>
         <xs:element ref="FirstName"/>
         <xs:element ref="LastName"/>
         <xs:element ref="Gender"/>
         <xs:element ref="YearOfBirth"/>
         <xs:element ref="Address"/>
         <xs:element ref="Valid"/>
      </xs:sequence>
   </xs:complexType>
   <xs:complexType name="T_Address">
      <xs:sequence>
         <xs:element ref="Street"/>
         <xs:element ref="PostalCode"/>
         <xs:element ref="City"/>
         <xs:element ref="StateProvince"/>
         <xs:element ref="Country"/>
      </xs:sequence>
   </xs:complexType>
   <xs:element name="YearOfBirth" type="xs:short"/>
   <xs:element name="Valid" type="T_Valid"/>
   <xs:element name="Street" type="xs:string"/>
   <xs:element name="StateProvince" type="xs:string"/>
   <xs:element name="PostalCode" type="xs:int"/>
   <xs:element name="LastName" type="xs:string"/>
   <xs:element name="Gender" type="T_Gender"/>
   <xs:element name="FirstName" type="xs:string"/>
   <xs:element name="Customer" type="T_Customer"/>
   <xs:element name="Country" type="xs:string"/>
   <xs:element name="City" type="xs:string"/>
   <xs:element name="Address" type="T_Address"/>
</xs:schema>

Соответственно, Oracle SQL запрос (в SQL Server этот запрос был бы на пару страниц неразбираемого текста):

SELECT XMLElement("Customer",
                  XMLElement("FirstName", c.cust_first_name),
                  XMLElement("LastName", c.cust_last_name),
                  XMLElement("Gender", c.cust_gender),
                  XMLElement("YearOfBirth", c.cust_year_of_birth),
                  XMLElement("Address",
                             XMLElement("Street", c.cust_street_address),
                             XMLElement("PostalCode", c.cust_postal_code),
                             XMLElement("City", c.cust_city),
                             XMLElement("StateProvince", c.cust_state_province),
                             XMLElement("Country",
                                        (SELECT country_name FROM sh.countries WHERE country_id = c.country_id)
                             )
                  ),
                  XMLElement("Valid", c.cust_valid)) AS DATA
  FROM sh.customers c
 WHERE c.cust_id = 43228

Вместо 43228, конечно, нужно сделать bindable переменную, но мне лень.

И его результат (Customer.xml):

<Customer>
   <FirstName>Abner</FirstName>
   <LastName>Everett</LastName>
   <Gender>M</Gender>
   <YearOfBirth>1957</YearOfBirth>
   <Address>
      <Street>117 West Gloucester Avenue</Street>
      <PostalCode>72059</PostalCode>
      <City>Los Angeles</City>
      <StateProvince>CA</StateProvince>
      <Country>United States of America</Country>
   </Address>
   <Valid>I</Valid>
</Customer>

Altova StyleVision

File -> New -> New from XML Schema / DTD..., указать путь к XSD файлу и желательно предоставить ему сразу файл-пример с данными, соответствующий этой схеме, конечно же.

После окончания создания шаблона (template.sps) нужно его сохранить в XSLT (transform-rtf.xslt): File -> Save Generated Files -> Save Generated XSLT-RTF File....

Saxon

What is Saxon?

The Saxon package is a collection of tools for processing XML documents. The main components are:

  • An XSLT 2.0 processor, which can be used from the command line, or invoked from an application, using a supplied API. This can also be used to run XSLT 1.0 stylesheets.
  • An XPath 2.0 processor accessible to applications via a supplied API.
  • An XQuery 1.0 processor that can be used from the command line, or invoked from an application by use of a supplied API.
  • An XML Schema 1.0 processor. This can be used on its own to validate a schema for correctness, or to validate a source document against the definitions in a schema. It is also used to support the schema-aware functionality of the XSLT and XQuery processors. Like the other tools, it can be run from the command line, or invoked from an application.
  • On the Java platform, when using XSLT, XPath, or XML schema validation, the API supported by Saxon is the JAXP API, which means it is possible for a Java application to switch between different XSLT, XPath, and XML Schema processors without changing the application code. At the time of writing, however, Saxon is still the only JAXP implementation that offers support for XSLT 2.0. Saxon offers two APIs for XQuery processing: its own native API, which has been present in the product for some time, and an early implementation of the XQJ specification which is under development as part of the Java Community Process.
  • On the .NET platform, Saxon offers an API that enables close integration with other services available from .NET, notably the XML-related classes in the System.Xml namespace. It isn't possible to use Saxon as a transparent plug-in replacement for the System.Xml.Xsl processor, because the API for the Microsoft engine using concrete classes rather than abstract interfaces. However, it is possible to use it as a functional replacement with minor changes to your application code.

Saxon implements XSLT 2.0, XPath 2.0, and XQuery 1.0 as defined in the final Recommendations of 23 January 2007. It also includes an implementation of the XML Schema 1.0 Recommendation.

Full details of Saxon's conformance to the specifications are provided in the Conformance section.

In addition, Saxon provides an extensive library of extensions, all implemented in conformance with the XSLT and XQuery Recommendations to ensure that portable stylesheets and queries can be written. These include the EXSLT extension libraries common, sets, math, and dates-and-times. Many of these extensions were pioneered in Saxon and have since become available in other products.

These extension functions are in general accessible from XQuery and XPath as well as XSLT, except where they depend on stylesheet information. Some extensions are available in Saxon-SA only.

Компиляция XSLT файла

Библиотека Saxon позволяет компилировать XSLT файл:

The term compile is stretching a point. The executable that is produced does not contain machine instructions, or even interpreted Java bytecode. It contains instructions in the form of a data structure that Saxon itself can interpret. Note that the format of compiled stylesheets is unlikely to be stable from one Saxon release to the next.

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

java -cp d:\Development\Libs\Java\Saxon.v9\saxon9.jar net.sf.saxon.Compile transform-rtf.xslt transform-rtf.saxon

transform-rtf.xslt - входной файл, transform-rtf.saxon - выходной.

Java код

Все просто - подключение к БД, выполнение запроса, получение потока XML, загрузка скомпилированного шаблона, создание трансформера и преобразование XML в RTF.

 0: /**
 1:  *
 2:  */
 3: package com.chabster.reporting.app;
 4:
 5: import java.io.File;
 6: import java.io.InputStream;
 7: import java.sql.Connection;
 8: import java.sql.DriverManager;
 9:
10: import javax.xml.transform.*;
11: import javax.xml.transform.stream.StreamResult;
12: import javax.xml.transform.stream.StreamSource;
13:
14: import net.sf.saxon.Configuration;
15: import net.sf.saxon.PreparedStylesheet;
16: import oracle.jdbc.OraclePreparedStatement;
17: import oracle.jdbc.OracleResultSet;
18: import oracle.jdbc.driver.OracleDriver;
19: import oracle.sql.OPAQUE;
20:
21: /**
22:  * @author Chabster
23:  */
24: public class Startup
25: {
26:
27:   static String query = "SELECT XMLElement(\"Customer\",\r\n" +
28:         "                 XMLElement(\"FirstName\", c.cust_first_name),\r\n" +
29:         "                 XMLElement(\"LastName\", c.cust_last_name),\r\n" +
30:         "                 XMLElement(\"Gender\", c.cust_gender),\r\n" +
31:         "                 XMLElement(\"YearOfBirth\", c.cust_year_of_birth),\r\n" +
32:         "                 XMLElement(\"Address\",\r\n" +
33:         "                            XMLElement(\"Street\", c.cust_street_address),\r\n" +
34:         "                            XMLElement(\"PostalCode\", c.cust_postal_code),\r\n" +
35:         "                            XMLElement(\"City\", c.cust_city),\r\n" +
36:         "                            XMLElement(\"StateProvince\", c.cust_state_province),\r\n" +
37:         "                            XMLElement(\"Country\",\r\n" +
38:         "                                       (SELECT country_name FROM sh.countries WHERE country_id = c.country_id)\r\n" +
39:         "                            )\r\n" +
40:         "                 ),\r\n" +
41:         "                 XMLElement(\"Valid\", c.cust_valid)) AS DATA\r\n" +
42:         "  FROM sh.customers c\r\n" +
43:         " WHERE c.cust_id = 43228";
44:
45:   public static void main(String[] args) {
46:      try {
47:         DriverManager.registerDriver(new OracleDriver());
48:
49:         Connection conn = DriverManager.getConnection("jdbc:oracle:thin:chabster/***@localhost:1521/MAIN");
50:         OraclePreparedStatement preparedStmt = (OraclePreparedStatement) conn.prepareStatement(query);
51:
52:         OracleResultSet oraRS = (OracleResultSet) preparedStmt.executeQuery();
53:         oraRS.next();
54:         OPAQUE xmlObj = oraRS.getOPAQUE(1);
55:         Configuration configuration = new Configuration();
56:         InputStream xmlStream = xmlObj.getStream();
57:
58:         PreparedStylesheet preparedStylesheet = PreparedStylesheet.loadCompiledStylesheet(configuration, "transform-rtf.saxon");
59:
60:         Transformer transformer = preparedStylesheet.newTransformer();
61:         File outputFile = new File("out.rtf");
62:         Source xmlSource = new StreamSource(xmlStream);
63:         Result output = new StreamResult(outputFile);
64:
65:         transformer.transform(xmlSource, output);
66:
67:         xmlStream.close();
68:         oraRS.close();
69:         preparedStmt.close();
70:         conn.close();
71:      }
72:      catch (Exception ex) {
73:         ex.printStackTrace();
74:      }
75:   }
76: }

Выводы

Механизм прост и удобен для разработчика. Генерация файлов происходит быстро, поддерживаются основные форматы документов - HTML, RTF и PDF. Правда, есть и свои нюансы, связанные с ограниченными возможностями дизайнера Altova StyleVision. Например, если в отчет нужно вставить картинку из БД, то прийдется это писать частично вручную. Также минимальный размер генерируемого файла - 46 Kb! Плюс вечно плавающие стандарты W3C и отсутствие библиотек, которые их поддерживают.

27 коммент.:

blog комментирует...

Как насчет JasperReports/iReport? Или возможно что-то другое Java-friendly с существующим дизайнером.

Chabster комментирует...

Он по функциональности напоминает Crystal Reports, а вот по сложности превышает на порядок. +Отсутствие нормальной документации. Читать здесь.

Есть еще BIRT, но, говорят, что тормозной. Я не пробовал.

Windward Reports привлекает ориентацией на Microsoft Word, ведь именно в таком формате пользователи хотят видеть отчеты.

Анонимный комментирует...

Ι think this is аmong the most ѵital info for
me. Αnd i'm glad reading your article. But wanna remark on few general things, The web site style is perfect, the articles is really great : D. Good job, cheers

Also visit my site :: Click Here

Анонимный комментирует...

Wοah! I'm really enjoying the template/theme of this website. It's simple, yet effеctiνe.
A lot of timеs it's very hard to get that "perfect balance" between usability and appearance. I must say that you'vе dοne a greаt job with this.
Adԁіtіonallу, thе blog loads ѕuper quick for
me on Oрera. Οutstаndіng Blog!


My website seo services lancaster

Анонимный комментирует...

Νow І am reаdy to do mу bгeakfast, later than having my brеaκfast coming оνeг аgаin tо
гeaԁ аdditіοnаl nеws.


Мy ωeb site: www.inalongdistancerelationship.com

Анонимный комментирует...

Whаt's up to all, how is all, I think every one is getting more from this web site, and your views are pleasant for new people.

Feel free to visit my webpage; wso reviews

Анонимный комментирует...

If sοme onе wishes expert view conсeгning runnіng а blog thеn i ѕuggest him/heг to vіsit
this weblog, Keep up the nicе work.

Also visit my web ѕite - get cash for Surveys Review scam
my web site :: get cash for surveys legit

Анонимный комментирует...

Hello іt's me, I am also visiting this web page daily, this site is genuinely good and the visitors are truly sharing pleasant thoughts.

Look at my web page; element human body
my web site - human body

Анонимный комментирует...

It is іn reality a nice and usеful ріecе οf іnfo.
Ι'm glad that you just shared this helpful information with us. Please keep us up to date like this. Thanks for sharing.

my web-site Article submitter Software

Анонимный комментирует...

Wοndеrful аrticle! We will be linking
to this pаrtіculaгly gгeat content
on оur ωebsite. Κeep uρ the grеat writing.



Check out my blοg ροst ... Www.Howtofindppl.Com

Анонимный комментирует...

Thanks for one's marvelous posting! I seriously enjoyed reading it, you will be a great author. I will always bookmark your blog and will come back very soon. I want to encourage yourself to continue your great work, have a nice afternoon!

Feel free to surf to my weblog; get ripped six pack abs fast

Анонимный комментирует...

Αdmіring thе timе and effoгt yоu put
into уouг blog аnd detailed information yοu presеnt.
Ιt's great to come across a blog every once in a while that isn't the same outdated rеhashеd mаterіal.

Great reaԁ! I've saved your site and I'm іncludіng youг RSЅ feeds to my Gоogle aсcοunt.


my blog post: Wso Software

Анонимный комментирует...

obvіοusly lіke your website however
уou need to takе a loοk аt thе spеlling on sevеrаl of уour posts.
Many οf them агe гifе with
spеlling problems аnd I in finԁіng it verу troublеsome to tеll thе reality then аgain I will сertainly cοme again again.


Feel free tο surf to my web site - internet dating advice for guys
my website: high school dating advice for guys

Анонимный комментирует...

Whаt's up i am kavin, its my first occasion to commenting anywhere, when i read this post i thought i could also make comment due to this brilliant piece of writing.

Feel free to visit my web page: http://www.getcash4surveys.info

Анонимный комментирует...

Havіng read thіs I thought it ωas rаtheг еnlіghtening.
Ӏ apprеciate уou tаking the time and еffort to ρut this cοntent tοgether.
I once agаin find mуself spending a sіgnifiсant amount of time both rеading anԁ commеnting.
But ѕо ωhаt, іt waѕ ѕtill worth іt!


My sіte ... article profits
Also see my web site: article Marketing campaign

Анонимный комментирует...

naturally lіke уour webѕite
but уou hаve tο takе a look at
the spеlling οn quite a fеω of your posts.
Many of them are rife with ѕрelling issuеѕ anԁ I fіnd it very bothersome to tеll the realіty neverthеless I will surelу comе bаck again.


mу web site ... go

Анонимный комментирует...

Τheѕe aгe aсtuаllу enormous iԁeaѕ іn about blogging.

You have tоuched some good poіntѕ here.

Anу way keep up wrіnting.

my blog :: the tao of badass
My web page: The Tao Of badass

Анонимный комментирует...

Hey there! I just wanted to ask if you ever have any problems
with hackers? My last blog (wordpress) was hacked and I ended up losing many months of hard work due to no back up.
Do you have any methods to prevent hackers?

My website - cheap loans

Анонимный комментирует...

Fіne waу of descгіbing, аnd fаstіdious article tо take data concerning mу presentatiοn foсus, whіch i am going tο ԁeliνеr in institutіon οf higher education.


Check οut my web page - get Kontent Machine

Анонимный комментирует...

What's up to every one, it's gеnuinely a good for
me to раy a quісκ viѕit this sіte,
it inсludes uѕeful Informatіon.


Μy weblog: gscraper vs scrapebox

Анонимный комментирует...

Ηello my frienԁ! I wish to say thаt this article iѕ awеsome, nicе wгitten and include almost all significant infos.
I'd like to see extra posts like this .

Feel free to surf to my blog jvzoo product of the day

Анонимный комментирует...

Nice аnswеrs in retuгn οf this iѕsue with real arguments anԁ tellіng evеrything about
that.

Feel frеe tο vіsit my blog pure leverage reviews

Анонимный комментирует...

Your wrіte-up offerѕ еstаblished necеssary tо me ρersonallу.
It’s reallу uѕeful and уou're clearly very educated in this field. You get opened up our eye to be able to different thoughts about this specific matter with intriguing, notable and solid content.

Have a look at my web site: Calozgroup.Org

Анонимный комментирует...

Great article, just what I was looking for.



Here is my website; http://klaniny.vot.pl

Анонимный комментирует...

It's amazing to go to see this website and reading the views of all colleagues about this post, while I am also keen of getting know-how.

my website ... bauchmuskelübungen

Анонимный комментирует...

Even so, researching health inѕurance could be lеss ԁifficult thаn it аppears.

An НSA is a low cost form of conventіonal health carе
covеrage. Makіng surе you have the
right mediсal coverage, including Medіcare suрplementаl insurance is еѕsential.


Ηave a look аt my ωeb blog :: Health Insurance Individual

Анонимный комментирует...

I absolutely love your blog and find the majority of your post's to be exactly what I'm
looking for. Would you offer guest writers to write content
for yourself? I wouldn't mind writing a post or elaborating on many of the subjects you write regarding here. Again, awesome blog!

Also visit my web page muscle pills

Отправить комментарий

Copyright 2007-2011 Chabster