Usar xml/xslt para catálogo productos en web e-commerce
En los primeros usos de xml se mencionan bien para crear documentos validados con un dtd/schema o bien usarlo para tranportar datos entre diferentes sistemas. En este segundo caso entra este ejemplo usado en la web elibros, para mantener catálogo de productos carrito de compra. Cuando creé esa aplicación, alrededor de 2002 en .net 1.1, usando la utilidad de simulación de carga de usuarios web stress tools, se obtenían mejores cifras para los usuarios concurrentes que podían acceder a la web usando xml y la posibilidad de cache que con acceso a la bb.dd sql server. Además posteriormente el haber usado xml facilitó el poder crear facturas en formato pdf, bastaba con recorer el xml. Pero lo mejor es verlo en el código usado.Empezamos con la parte de catálogo, tenemos la cabecera declarando el tiempo de cache, la inclusión de espacios de nombres a incluir y la conexión a la bb.dd. Como puede verse el fichero xml se crear como un stream.
<%@ Page Language="VB"%> <%@ Outputcache Duration="86400" VaryByParam="none" %> <%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <%@ Import Namespace="System.IO" %> <%@ Import Namespace="System.Text" %> <%@Import Namespace="System.Xml.Xpath" %> <%@Import Namespace="System.Xml" %> <%@Import Namespace="System.Xml.Xsl" %> <%@ Register TagPrefix="ctrldb" TagName="conecta" Src="conectadb.ascx" %> <ctrldb:conecta id="ctlConnectStrings" runat="server"/> <%-- usar esta función para en xml/xstl generar urls amigables en el bucle que lee de la base datos crear un elemento con el titulo espacios cambiados por - Dim TestString As String = "Shopping List" ' Returns "Shipping List". Dim tituloenlace As String = Replace(Trim(mireader("Product_Name")), " ", "-") en la hoja de estilo productos.xsl cambiar <xsl:attribute name="href">/compra/additem<xsl:value-of select="ID"/>/<xsl:value-of select="TituloEnlace"/>.html --%> .... .... <script language="VB" runat="server"> sub Page_Load() dim strpathonly as string dim fichero, ficheroxsl as string dim strSqlpro as string dim strconn as string dim xmlstr as string dim objstream as stream dim objTransform As New XslTransform() strconn=ctlConnectStrings.SQLConnectionString strSqlpro="select * from product" strpathonly=Request.PhysicalPath fichero= Left(strpathonly, InStrRev(strpathonly, "")) & "productos.xml" ficheroxsl= Left(strpathonly, InStrRev(strpathonly, "")) & "productos.xsl" dim miconexion as SqlConnection = new sqlConnection(strconn) miconexion.Open() dim miSqlCommand as SqlCommand = new SqlCommand() miSqlCommand.Connection = miconexion miSqlCommand.CommandText =strSqlpro Dim mireader as SqlDataReader mireader=miSqlCommand.ExecuteReader() if not File.Exists(fichero) then xmlstr = "<?xml version=""1.0""?>" & vbCrLf xmlstr = xmlstr & "<Tienda>" & vbCrLf While mireader.Read() xmlstr = xmlstr & vbtab & "<Producto>" & vbcrlf & vbtab & vbtab & "<Nombre>" & Trim(mireader("Product_Name")) & "</Nombre>" & vbcrlf & vbtab & vbtab & "<TituloEnlace>" & Replace(Trim(mireader("Product_Name")), " ", "-") & "</TituloEnlace>" & vbcrlf & vbtab & vbtab & "<ID>" & Trim(mireader("product_id")) & "</ID>" & vbcrlf & vbtab & vbtab & "<producto_Descripcion>" & Trim(mireader("product_description")) & "</producto_Descripcion>" & vbcrlf xmlstr = xmlstr & vbtab & vbtab & "<Precio>" & FormatNumber(mireader("price"),2, , ,TriState.True) & "</Precio>" & vbcrlf & vbtab & vbtab & "<Descuento>" & FormatNumber(mireader("discount"),2, , ,TriState.True) & "</Descuento>" & vbcrlf & vbtab & vbtab & vbCrLf xmlstr = xmlstr & vbtab & "</Producto>" & vbCrLf end While xmlstr = xmlstr & vbtab & "</Tienda>" & vbcrlf mireader.close() miconexion.close() objstream=File.OpenWrite(fichero) dim xmlfi as new StreamWriter(objstream,System.Text.Encoding.unicode) xmlfi.write(xmlstr) xmlfi.close() objTransform.Load(ficheroxsl) dim objXPDoc as New XPathDocument(fichero) dim writer As New XmlTextWriter(Response.Output) objTransform.Transform(objXPDoc, Nothing, writer) else objTransform.Load(ficheroxsl) dim objXPDoc as New XPathDocument(fichero) dim writer As New XmlTextWriter(Response.OutputStream, System.Text.Encoding.utf8) objTransform.Transform(objXPDoc, Nothing, writer) end if end sub </script>
En principio como ya he comentado el interés de usar xml era la ventaja que daba para el número de usuarios que podía servir la aplicación. Pero una posterior modificación de la web para poder crear facturas en pdf mostró lo sencillo que era crearlas partiendo de tener los pedidos en xml. Para ello uso la librería ITextSharp
Imports System Imports System.IO Imports System.Xml Imports System.Xml.XPath Imports iTextSharp.text Imports iTextSharp.text.pdf Partial Class ShowPDF Inherits System.Web.UI.Page Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load ShowTable() End Sub Sub ShowTable() Dim cartid As String If Not (Request.Cookies("cartid") Is Nothing) Then If (Request.Cookies("cartid").Value <> "") Then cartid = Request.Cookies("cartid").Value End If End If Dim fichxml As String fichxml = "cart" + cartid + ".xml" Dim fichpdf As String fichpdf = "fact" + cartid + ".pdf" Dim xpathdoc As XPathDocument = New XPathDocument(Server.MapPath(fichxml)) Dim navigator As XPathNavigator = xpathdoc.CreateNavigator() Dim subnavigator As XPathNavigator = xpathdoc.CreateNavigator() Dim nodes As XPathNodeIterator = navigator.Select("/Cart/Producto") Dim subnode As XPathNodeIterator = subnavigator.Select("/Cart/Subtotal") Dim cabecera As String Dim subtotal As String Dim subxmldoc As XmlDocument = New XmlDocument subxmldoc.Load(Server.MapPath(fichxml)) subtotal = "Total: " + subxmldoc.SelectSingleNode("/Cart/Subtotal").InnerText + " Euros" cabecera = "Elibros factura ref. " + cartid Dim doc As Document = New Document PdfWriter.GetInstance(doc, New FileStream(Request.PhysicalApplicationPath + fichpdf, FileMode.Create)) doc.Open() Dim table As Table = New Table(6) table.BorderWidth = 1 table.BorderColor = New Color(0, 0, 255) table.Padding = 3 table.Spacing = 1 Dim cell As Cell = New Cell(cabecera) cell.Header = True cell.Colspan = 6 table.AddCell(cell) table.AddCell("ID Libro") table.AddCell("Título") table.AddCell("Detalle") table.AddCell("Precio €") table.AddCell("Cantidad") table.AddCell("Subtotal €") While (nodes.MoveNext()) table.AddCell(nodes.Current.SelectSingleNode("ItemId").Value) table.AddCell(nodes.Current.SelectSingleNode("Nombre").Value) table.AddCell(nodes.Current.SelectSingleNode("producto_Descripcion").Value) table.AddCell(nodes.Current.SelectSingleNode("Precio").Value) table.AddCell(nodes.Current.SelectSingleNode("Cantidad").Value) table.AddCell(nodes.Current.SelectSingleNode("ItemSubTotal").Value) End While 'subtotal = "Subtotal: " + subnode.Current.("Subtotal").Value Dim cellsub As Cell = New Cell(subtotal) cellsub.Colspan = 6 table.AddCell(cellsub) doc.Add(table) doc.Close() Response.Redirect(fichpdf) End Sub End Class
En este segundo caso se han usado expresiones xpath para el acceso a los elementos del fichero xml.