2013. május 23., csütörtök

Browsers and XSL transformations

Browser side XSL transformations has two ways. You can do this with JavaScript, or native browser function. The native way supported well in today's modern browsers (IE, FireFox, Opera, Safari, Chrome). With XML header contained transformation you can easy make a teszt.
Show follow data.xml :

<?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">&#60;?xml version='1.0'
    encoding='utf-8'?>&#60;containerdescriptor
    classname='com.infokristaly.engine.containers.SQLContainer'>
    &#60;attribute name='pagesize'>15&#60;/attribute>&#60;attribute
    name='autoopen'>true&#60;/attribute>&#60;table>
    &#60;sqlstring>select id, megnevezes from
    demand_kategoria&#60;/sqlstring>
    &#60;fieldlist>
    &#60;field name='id' type='java.lang.Integer' order='1' primarykey='true'
    visible='false'>
    &#60;label context='HUN'>azonosító&#60;/label>
    &#60;/field>
    &#60;field name='megnevezes' type='java.lang.String' order='2' visible='true'>
    &#60;label context='HUN'>Kategória&#60;/label>
    &#60;/field>
    &#60;/fieldlist>
    &#60;/table>&#60;/containerdescriptor></field>
  </rec>
  <rec>
   <field name="id">2</field>
   <field name="name">arlista</field>
   <field name="descriptor">&#60;?xml version='1.0'
    encoding='utf-8'?>&#60;containerdescriptor
    classname='com.infokristaly.engine.containers.SQLContainer'>&#60;table>
    &#60;sqlstring>select kategoria_id, kod1, kod2, megnevezes, allapot,
    partner_netto, fogyaszto_netto, fogyaszto_brutto, validfrom from
    demand_arlista where kategoria_id = ?&#60;/sqlstring>
    &#60;fieldlist>
    &#60;field name='kategoria_id' type='java.lang.Integer' order='1'
    primarykey='true' visible='false'>
    &#60;label context='HUN'>Kategória azonosító&#60;/label>
    &#60;/field>
    &#60;field name='kod1' type='java.lang.String' order='2' visible='true'>
    &#60;label context='HUN'>Termékcsoport kód&#60;/label>
    &#60;/field>
    &#60;field name='kod2' type='java.lang.String' order='3' primarykey='true'
    visible='true'>
    &#60;label context='HUN'>Termék azonosító&#60;/label>
    &#60;/field>
    &#60;field name='megnevezes' type='java.lang.String' order='4' visible='true'>
    &#60;label context='HUN'>Megnevezés&#60;/label>
    &#60;/field>
    &#60;field name='allapot' type='java.lang.String' order='5' visible='true'>
    &#60;label context='HUN'>Állapot&#60;/label>
    &#60;/field>
    &#60;field name='partner_netto' type='java.lang.Integer' order='6'
    visible='true'>
    &#60;label context='HUN'>Partner ár (nettó HUF)&#60;/label>
    &#60;/field>
    &#60;field name='fogyaszto_netto' type='java.lang.Integer' order='7'
    visible='true'>
    &#60;label context='HUN'>Fogyasztói ár (nettó HUF)&#60;/label>
    &#60;/field>
    &#60;field name='fogyaszto_brutto' type='java.lang.Integer' order='8'
    visible='true'>
    &#60;label context='HUN'>Fogyasztói ár (bruttó HUF)&#60;/label>
    &#60;/field>
    &#60;field name='validfrom' type='java.util.Date'
    view='com.infokristaly.views.fields.SimpleDateFormatView' order='9'
    visible='true'>
    &#60;label context='HUN'>Érvényes (-tól)&#60;/label>
    &#60;/field>
    &#60;/fieldlist>
    &#60;references>&#60;container source='./kategoria' fieldname='id'/>&#60;/references>
    &#60;/table>&#60;/containerdescriptor></field>
  </rec>
 </data>
</xml>

You can see the table.xsl in header of data.xml, it makes the browsers to transform dry xml to other new DOM like a displayable HTML, or processable CSV or similars, like Excel 2003 xlsx.

We stay at HTML. For this we'll use the table.xsl:

<?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

Browser side JavaScript has extra classes like FireFox has XSLTProcessor and IE has ActiveXObject that can use MSXML2.XSLTemplate extension. With this and a little JQuery helper (for example AJAX requests) it is easy generate a HTML table from XML. Let see a FireFox homework:

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);
 }
}
The XML generator servlet is run on JBoss 7 AS that has a configured demandDS datasource. The servlet code:

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();
    }
 
}
 
The container test4transform.html is as simply as a HTML5:

<!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>

In the future maybe will be world-wide dynamic pages that will use this transformations. As a linked in CSS and JavaScript files, XSL files are stored in browser cache, so the network trafic can be lower then before. The handicap is in development and debugging. There is no utility - like FireBug - for test transformations. There are commercial tools for external XML/XSL, but in browser market this is a big disadvantage and it's very wanted tool. I hope the solution will not so fare. Standard DOM and JavaScript spread progressively. Maybe this development trend will have interests like standart HTML4, and the browser manufacturers will implement this utility very quick.

Nincsenek megjegyzések:

Megjegyzés küldése