Kontynuując zapoczątkowany w naszym poprzednim artykule temat języka XSLT (Extensible Stylesheet Language Transformations) tym razem skupmy się na faktycznej efektywności tego języka w konkretnych zastosowaniach. W przypadku przenoszenia poprawnie sformułowanych dokumentów XML o zbliżonej logice, charakteryzuje się on wyjątkową prostotą, szybkością działania, jak również i przenośnością. Czy jednak zawsze transformacje XSL są tak skuteczne? Poniższy artykuł ma za zadanie odpowiedzieć na to pytanie. Skierowany jest on do kierowników zespołów, osób odpowiedzialnych za technologie w firmach i wszystkich zainteresowanych co dzieje się "pod maską" programu na który składa się blisko 300 tysięcy linii transformacji XSLT.
W trakcie trwającej ponad rok pracy kilkuosobowego zespołu programistów firmy Clever Age nad zleconą przez Microsoft wtyczką do otwierania plików ODF/zapisywania plików jako ODF zdecydowana większość pracy poświęcona była właśnie na przekształcenia XSLT między formatami OpenDocument i OpenXML. Powstaje tu pytanie, jakie aspekty technologii XSLT wymagały tak wielkich nakładów pracy? Aby na nie odpowiedzieć przedstawimy kilka przykładowych problemów technicznych, przed jakimi stanęli programiści podczas pracy nad tym zadaniem. Problemy są podparte przykładami z autentycznego kodu wtyczki, który każdy może pobrać i zainstalować z oficialnej witryny poświęconej konwerterowi.
W formacie OpenXML dany format liczbowy jest po prostu wartością atrybutu formatCode elementu "numFmt", natomiast w formacie OpenDocument na tenże format składa się wiele elementów, z których nadrzędnym jest "number:number-style", jeżeli jest to format zwykłej liczby, "number:percentage-style", jeżeli jest to format procentowy lub też "number:currency-style", jeżeli mamy do czynienia z formatem walutowym. Przykładowo, format przedstawiający liczbę 1 jako 01,000 jest w OpenDocument przedstawiony jako:
<number:number-style style:name="N107">
<number:number number:decimal-places="3" number:min-integer-digits="2" number:grouping="true"/>
</number:number-style>
natomiast w OpenXML jako:
<numFmt numFmtId="2" formatCode="#,#00.000"/&rt;
Konwersja formatów liczbowych z OpenDocument na OpenXML wymagała więc stworzenia szablonów pasujących do każdego z elementów "number:number-style", "number:percentage-style" i "number:currency-style". Dodatkowo, każdy z tych szablonów musi wywoływać nazwany szablon GetFormatCode, który zajmuje się przetwarzaniem podelementów, które są wspólne dla wszystkich formatów i zamianą ich na znaki tworzące atrybut "formatCode". Szablon ten jest bardzo skomplikowany, gdyż musi uwzględniać wszystkie możliwe przypadki, nawet tak niestandardowe jak znaki tekstowe "wewnątrz" formatu liczbowego. Zawiera on również bardzo obciążające XSLT rekurencyjne podszablony, które okazują się być mimo wszystko najlepszym sposobem na konwersję takich elementów formatu jak np. miejsca po przecinku. Niestety konwersja tychże formatów z OpenXML na OpenDocument okazuje się być równie trudna.
Kolejnym problemem wynikającym z różnic między formatami jest problem konwersji wielopoziomowych numerowanych list. W formacie OpenXML paragraf z napisem "przykładowy" będący elementem wielopoziomowej listy znajdującym się na poziomie 2 wyglądałby nastepująco:
<w:p w:rsidR="00B500A6" w:rsidRDefault="00B500A6" w:rsidP="00B500A6">
<w:pPr>
<w:pStyle w:val="ListParagraph"/>
<w:numPr>
<w:ilvl w:val="1"/>
<w:numId w:val="2"/>
</w:numPr>
</w:pPr>
<w:proofErr w:type="spellStart"/>
<w:r>
<w:t>Przykładowy</w:t>
</w:r>
<w:proofErr w:type="spellEnd"/>
</w:p>
Elementem określającym fakt, że paragraf jest elementem numerowanej listy jest "w:numId", poziom na którym się znajduje, określa element "w:ilvl" a wartość atrybutu w:val="1" oznacza, że znajduje się on na poziomie 2 (poziomowi 1 odpowiada wartość 0).
W formacie OpenDocument natomiast, ów napis będzie wyglądał tak:
<text:list-item>
<text:list text:style-name="L1" text:continue-numbering="true">
<text:list-item>
<text:p text:style-name="ID0EW">Przykładowy</text:p>
</text:list-item>
</text:list>
</text:list-item>
Tutaj element okreslający paragraf "Przykładowy" jest umieszczony wewnątrz elementu "text:list-item" określającego element listy określanej elementem "text:list". Aby uzyskać efekt "poziomu drugiego", lista ta musiała zostać umieszczona w elemencie "text:list-item" listy znajdującej sie na poziomie pierwszym. Konwersja OpenXML->ODF okazuje się więc również w tym przypadku nietrywialna i wymaga rekurencyjnego zastosowania m.in. trzech nazwanych template'ów InsertCurrentLevel (wstawiającego nową listę/element na tym samym poziomie co poprzedni), InsertFollowingLevel (wstawiającego nową listę/element na poziomie niższym niż poprzedni) i InsertListLevel(faktycznie wstawiającego nową listę/element) i skomplikowanych warunków określających, który z tychże szablonów powinien być aktualnie zastosowany. Również w tym przypadku także konwersja ODF->OpenXML nie mogła być zaimplementowana w prosty sposób.
Różnice między formatami niejednokrotnie okazywały się na tyle duże, że konwersja w XSLT okazywała się zbyt trudna do zaimplementowania, zbyt czasochłonna w działaniu, lub po prostu niemożliwa do zrealizowania. W takim wypadku jedynym rozwiązaniem okazywał się post-procesor.
Post-procesor jest mechanizmem wykonującym końcowe operacje przetwarzania pliku XML powstałego po zastosowaniu przekształceń XSLT. Przetwarza on wynikowy XML metodą zdarzeniową, gdzie każde otwarcie/zamknięcie znacznika/atrybutu jest interpretowane jako zdarzenie. Został on zaimplementowany w języku C#. Język ten wymaga jednak zainstalowania środowiska .NET framework, przeznaczonego głównie dla Microsoft Windows. W ten sposób aplikacja opierająca się na przekształceniach XSLT, traci jedną z najwiekszych zalet języka XSLT, a mianowicie jego przenośność. Prowadzi to nieuchronnie do konkluzji, że zalety języka XSLT mogą być w pełni wykorzystane tylko jeżeli jest on używany do konwersji dokumentów XMLowych, których format wejściowy i wyjściowy są tworzone wg. podobnej logiki, a gdy tak nie jest, jego zastosowanie okazuje się być niezwykle kosztowne.