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.