1.3.3. fejezet, XSLT
A böngésző oldali XSL transzformációknak két változatáról írok. Ez lehet JavaScript alapú, és böngésző alapú. A böngésző alapú jobban támogatott, a mai modern böngészők (IE, FireFox, Opera, Safari, Chrome) mind alkalmazzák az xml fejlécként megadható transzformációt.
Az data.xml a következő lehet:
<?xml version="1.0" encoding="utf-8"?> <?xml-stylesheet href='xsl/table.xsl' type='text/xsl'?> <xml> <data> <rec> <field name="id">1</field> <field name="name">kategoria</field> <field name="descriptor"><?xml version='1.0' encoding='utf-8'?><containerdescriptor classname='com.infokristaly.engine.containers.SQLContainer'> <attribute name='pagesize'>15</attribute><attribute name='autoopen'>true</attribute><table> <sqlstring>select id, megnevezes from demand_kategoria</sqlstring> <fieldlist> <field name='id' type='java.lang.Integer' order='1' primarykey='true' visible='false'> <label context='HUN'>azonosító</label> </field> <field name='megnevezes' type='java.lang.String' order='2' visible='true'> <label context='HUN'>Kategória</label> </field> </fieldlist> </table></containerdescriptor></field> </rec> <rec> <field name="id">2</field> <field name="name">arlista</field> <field name="descriptor"><?xml version='1.0' encoding='utf-8'?><containerdescriptor classname='com.infokristaly.engine.containers.SQLContainer'><table> <sqlstring>select kategoria_id, kod1, kod2, megnevezes, allapot, partner_netto, fogyaszto_netto, fogyaszto_brutto, validfrom from demand_arlista where kategoria_id = ?</sqlstring> <fieldlist> <field name='kategoria_id' type='java.lang.Integer' order='1' primarykey='true' visible='false'> <label context='HUN'>Kategória azonosító</label> </field> <field name='kod1' type='java.lang.String' order='2' visible='true'> <label context='HUN'>Termékcsoport kód</label> </field> <field name='kod2' type='java.lang.String' order='3' primarykey='true' visible='true'> <label context='HUN'>Termék azonosító</label> </field> <field name='megnevezes' type='java.lang.String' order='4' visible='true'> <label context='HUN'>Megnevezés</label> </field> <field name='allapot' type='java.lang.String' order='5' visible='true'> <label context='HUN'>Állapot</label> </field> <field name='partner_netto' type='java.lang.Integer' order='6' visible='true'> <label context='HUN'>Partner ár (nettó HUF)</label> </field> <field name='fogyaszto_netto' type='java.lang.Integer' order='7' visible='true'> <label context='HUN'>Fogyasztói ár (nettó HUF)</label> </field> <field name='fogyaszto_brutto' type='java.lang.Integer' order='8' visible='true'> <label context='HUN'>Fogyasztói ár (bruttó HUF)</label> </field> <field name='validfrom' type='java.util.Date' view='com.infokristaly.views.fields.SimpleDateFormatView' order='9' visible='true'> <label context='HUN'>Érvényes (-tól)</label> </field> </fieldlist> <references><container source='./kategoria' fieldname='id'/></references> </table></containerdescriptor></field> </rec> <rec> <field name="id">3</field> <field name="name">arlistaszerk</field> <field name="descriptor"><?xml version='1.0' encoding='utf-8'?><containerdescriptor classname='com.infokristaly.engine.containers.MemoryContainer'> <attribute name='autoopen'>true</attribute><table> <fieldlist> <field name='kategoria_id' type='java.lang.Integer' order='1' primarykey='true' visible='true'> <label context='HUN'>Kategória azonosító</label> </field> <field name='kod1' type='java.lang.String' order='2' visible='true'> <label context='HUN'>Termékcsoport kód</label> </field> <field name='kod2' type='java.lang.String' order='3' primarykey='true' visible='true'> <label context='HUN'>Termék azonosító</label> </field> <field name='megnevezes' type='java.lang.String' order='4' visible='true' editortype='textarea' editoroptions='rows=10 cols=80'> <label context='HUN'>Megnevezés</label> </field> <field name='allapot' type='java.lang.String' order='5' visible='true'> <label context='HUN'>Állapot</label> </field> <field name='partner_netto' type='java.lang.Integer' order='6' visible='true'> <label context='HUN'>Partner ár (nettó HUF)</label> </field> <field name='fogyaszto_netto' type='java.lang.Integer' order='7' visible='true'> <label context='HUN'>Fogyasztói ár (nettó HUF)</label> </field> <field name='fogyaszto_brutto' type='java.lang.Integer' order='8' visible='true'> <label context='HUN'>Fogyasztói ár (bruttó HUF)</label> </field> <field name='validfrom' type='java.lang.String' order='9' visible='true'> <label context='HUN'>Érvényes (-tól)</label> </field> </fieldlist> </table></containerdescriptor></field> </rec> <rec> <field name="id">4</field> <field name="name">kategoria</field> <field name="descriptor"><?xml version='1.0' encoding='utf-8'?><containerdescriptor classname='com.infokristaly.engine.containers.SQLContainer'> <attribute name='pagesize'>10</attribute><attribute name='autoopen'>true</attribute><table> <sqlstring>select id, megnevezes from demand_kategoria</sqlstring> <fieldlist> <field name='id' type='java.lang.Integer' order='1' primarykey='true' visible='false'> <label context='HUN'>azonosító</label> </field> <field name='megnevezes' type='java.lang.String' order='2' visible='true'> <label context='HUN'>Kategória</label> </field> </fieldlist> </table></containerdescriptor></field> </rec> <rec> <field name="id">5</field> <field name="name">level_lista</field> <field name="descriptor"><?xml version='1.0' encoding='utf-8'?><containerdescriptor classname='com.infokristaly.engine.containers.MemoryContainer'> <attribute name='pagesize'>10</attribute><attribute name='autoopen'>true</attribute><table> <fieldlist> <field name='messagenumber' type='java.lang.Integer' order='1' visible='true'> <label context='HUN'>Azonosító</label> </field> <field name='date' type='java.util.Date' order='2' visible='true'> <label context='HUN'>Dátum</label> </field> <field name='from' type='java.lang.String' order='3' visible='true'> <label context='HUN'>Feladó</label> </field> <field name='subject' type='java.lang.String' order='4' visible='true'> <label context='HUN'>Tárgy</label> </field> </fieldlist> </table></containerdescriptor></field> </rec> <rec> <field name="id">6</field> <field name="name">dbfcontainer</field> <field name="descriptor"><?xml version='1.0' encoding='utf-8'?><containerdescriptor classname='com.infokristaly.engine.containers.CachedReadOnlyDBFContainer'> <table> <fieldlist> <field name='kulcs' type='java.lang.Integer' order='1' visible='true'> <label context='HUN'>Kulcs</label> </field> <field name='cim' type='java.lang.String' order='2' visible='true'> <label context='HUN'>Cím</label> </field> <field name='mutato' type='java.lang.Integer' order='3' visible='true'> <label context='HUN'>Mutató</label> </field> </fieldlist> </table></containerdescriptor> </field> </rec> </data> </xml>
A fejlécben látszik a table.xsl, ami arra készteti a böngészőket, hogy a száraz tényeket tartalmazó xml-ből valami újat készítsen, mondjuk egy általánosan megjeleníthető HTML-t, de lehet ez akár egy CSV vagy egy Excel 2003 XLS xml. Maradjunk egy egyszerű HTML-nél. Ehhez használjuk a következő table.xsl-t:
<?xml version="1.0" encoding='utf-8'?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output method="html" indent="yes" /> <xsl:template match="/xml/data"> <table width="100%"> <thead> <tr> <td>id</td> <td>name</td> <td>description</td> </tr> </thead> <tbody> <xsl:for-each select="./rec"> <xsl:element name="tr"> <xsl:variable name="row" select="./field" /> <td valign='top'> <xsl:value-of select="$row[@name='id']" /> </td> <td valign='top'> <xsl:value-of select="$row[@name='name']" /> </td> <td valign='top'> <xsl:value-of select="$row[@name='descriptor']" /> </td> </xsl:element> </xsl:for-each> </tbody> </table> </xsl:template> </xsl:stylesheet>
JavaScript
Böngésző oldali JavaScript-et használva az átalakításhoz rendelkezésünkre áll FireFox-ban egy XSLTProcessor nevű osztály. IE-ben ActiveXObject-ként elérhető a MSXML2.XSLTemplate kiegészítő. Ezekkel, és egy kis JQuery segítséggel (pl.: AJAX kérésekkel) egyszerűen leképezhető egy táblázat az XML-ből. Nézzünk egy példát FireFox-ra:
function doTransform() { var XMLpath = "XMLGenerator?xsl=jquery"; var XSLpath = "xsl/table.xsl"; var xslRef; var xmlRef; $.ajax({ type : "GET", url : XSLpath, async : false, dataType : "xml", success : function(xsl) { xslRef = xsl; } }); $.ajax({ type : "GET", url : XMLpath, async : false, dataType : "xml", success : function(xml) { xmlRef = xml; } }); if ((xmlRef != null) && (xslRef != null)) { containerDoc = document.implementation.createDocument('', "xml", null); $(xmlRef).find('>').each(function(index) { containerDoc.firstChild.appendChild(this); }); var xsltProcessor = new XSLTProcessor(); xsltProcessor.reset(); xsltProcessor.importStylesheet(xslRef); var fragment = xsltProcessor.transformToFragment(containerDoc, document); $("#content").html(fragment); } }
Az XML generátor Servlet egy JBoss 7 AS-en fut, amiben konfigurálva van a demandDS datasource. A servlet kódja:
package hu.infokristaly.homework.dynamicweb; import hu.infokristaly.homework.dynamicweb.utils.StrTk; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Hashtable; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.sql.DataSource; @WebServlet("/XMLGenerator") public class XMLGenerator extends HttpServlet { private static final long serialVersionUID = -6851012218039126751L; public XMLGenerator() { super(); } public static DataSource getDataSource() { DataSource result = null; try { Hashtable<String, String> env = new Hashtable<String, String>(11); InitialContext ctx = new InitialContext(env); Context envContext = (Context) ctx.lookup("java:jboss"); result = (DataSource) envContext.lookup("datasources/demandDS"); } catch (NamingException e) { e.printStackTrace(); } return result; } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ByteArrayOutputStream ostream = new ByteArrayOutputStream(); OutputStreamWriter sout = new OutputStreamWriter(ostream, "utf-8"); sout.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>"); if (!"jquery".equals(request.getParameter("xsl"))) { sout.write("<?xml-stylesheet href='xsl/table.xsl' type='text/xsl'?>"); sout.write("<xml>"); } sout.write("<data>"); DataSource ds = getDataSource(); try { Statement stmt = ds.getConnection().createStatement(); if (stmt.execute("select id,name,descriptorXML from ContainerDescriptor")) { ResultSet rs = stmt.getResultSet(); while (rs.next()) { sout.write("<rec>"); sout.write("<field name=\"id\">" + rs.getLong(1) + "</field>"); sout.write("<field name=\"name\">" + StrTk.HTMLEnc(rs.getString(2)) + "</field>"); sout.write("<field name=\"descriptor\">" + StrTk.HTMLEnc(rs.getString(3)) + "</field>"); sout.write("</rec>"); } rs.close(); stmt.close(); } } catch (SQLException e) { e.printStackTrace(); } sout.write("</data>"); if (!"jquery".equals(request.getParameter("xsl"))) { sout.write("</xml>"); } sout.flush(); response.setCharacterEncoding("utf-8"); response.setContentType("text/xml"); ServletOutputStream out = response.getOutputStream(); ostream.writeTo(out); out.flush(); out.close(); } }
Maga a test4transform.html pedig a következő:
<!DOCTYPE html> <html> <head> <script type="text/javascript" src="js/jquery.js"></script> <script type="text/javascript" src="js/transform.js"></script> <meta charset="UTF-8"> <title>Transform JQuery AJAX XML/XSLT</title> </head> <body> <button onclick="doTransform()">Transform</button> <p id="content"></p> </body> </html>
Talán a jövőben lesznek olyan, általánosan elterjedt dinamikus oldalak, amik ki fogják használni ezt a technikát. Ahogy a belinkelt CSS és a JavaScript fájlok, az XSL-ek is a böngésző cache-be kerülnek, így még hatékonyabban csökkentik az adatforgalmat. Hátránya viszont van, amit főleg hibakeresésnél érzünk meg. Ugyanis a FireBug-hoz hasonló transzformáció követő még nincs a piacon, már pedig erre nagy szükség lenne. De bizakodjunk, hiszen a szabványos DOM és a JavaScript elterjedése is fokozatosan alakult ki. Talán ez a fejlesztési irány is kap akkora figyelmet, mint annak idején a szabványosított HTML4, és fokozatosan bekerül a böngészőkbe egy ilyen eszköz.
- A hozzászóláshoz be kell jelentkezni