Escenario bancario con XSLT
Sumario
- 1 Enunciado
- 2 Análise do problema
- 3 Solución paso a paso
- 3.1 Paso inicial - elemento raíz baleiro
- 3.2 Paso 2. Xenerar elementos cuenta e fondo
- 3.3 [Cuentas]. Paso 1. Movendo elementos cuenta
- 3.4 [Cuentas]. Paso 2. Meter dentro de atributo dnititular
- 3.5 [Cuentas]. Paso 3. Extraer valor do atributuo dni
- 3.6 [Cuentas]. Paso 4. Crear elemento titular
- 3.7 [Cuentas]. Paso 5. Crear elemento saldoactual
- 3.8 [Cuentas]. Paso 7. Engadimos data de creación
- 3.9 [Fondo] Paso 1. Crear elemento fondo
- 3.10 [Fondo] Paso 2. Poner el atributo cuentaasociado
- 3.11 [Fondo] Paso 3. Crear el elemento cantidaddepositada
- 3.12 [Fondo] Paso 4. Crear o elemento moneda
- 4 Solución final
- 5 Referencias
Enunciado
Unha empresa utiliza o seguiente XML para intercambiar información entre bases de datos de distintos proveedores.
Estrutura orixinal:
<!--Estructura original de la información-->
<listado>
<cuenta>
<titular dni="5671001D">Ramon Perez</titular>
<saldoactual moneda="euros">12000</saldoactual>
<fechacreacion>13-abril-2012</fechacreacion>
</cuenta>
<fondo>
<cuentaasociada>20-A</cuentaasociada>
<datos>
<cantidaddepositada>20000</cantidaddepositada>
<moneda>Euros</moneda>
</datos>
</fondo>
<fondo>
<cuentaasociada>21-DX</cuentaasociada>
<datos>
<cantidaddepositada>4800</cantidaddepositada>
<moneda>Dolares</moneda>
</datos>
</fondo>
<cuenta>
<titular dni="39812341C">Carmen Diaz</titular>
<saldoactual moneda="euros">1900</saldoactual>
<fechacreacion>15-febrero-2011</fechacreacion>
</cuenta>
</listado>
Compran un novo sistema que necesita que a información teña a seguinte estrutura.
Estrutura que deben ter os datos:
<!--Estructura final que debemos conseguir-->
<datos>
<cuentas>
<cuenta dnititular="5671001D">
<creacion>13-abril-2012</creacion>
<titular>Ramon Perez</titular>
<saldoactual>12000 euros</saldoactual>
</cuenta>
<cuenta dnititular="39812341C">
<creacion>15-febrero-2011</creacion>
<titular>Carmen Diaz</titular>
<saldoactual>1900 euros</saldoactual>
</cuenta>
</cuentas>
<fondos>
<fondo cuentaasociada="20-A">
<cantidaddepositada>20000</cantidaddepositada>
<moneda>Euros</moneda>
</fondo>
<fondo cuentaasociada="21-DX">
<cantidaddepositada>4800</cantidaddepositada>
<moneda>Dolares</moneda>
</fondo>
</fondos>
</datos>
Crea o XSLT que permita convertir a información orixinal no novo formato requirido polo sistema.
Análise do problema
Precísanse varios cambios:
- Se ha cambiado el nombre de elemento raíz de listado a datos.
- Ahora todos los elementos cuenta van dentro de un nuevo elemento cuentas y todos los elementos fondo van dentro de un nuevo elemento fondos.
- El dni se ha movido del elemento titular al elemento cuenta.
- La fechacreación se ha movido y se ha renombrado a creacion.
- El elememento moneda desaparece y su texto se ha puesto al lado de la cantidad que hay en saldoactual.
- En el elemento fondo se ha quitado el elemento datos.
- El elemento cuentaasociada ha pasado a ser un atributo.
Solución paso a paso
Paso inicial - elemento raíz baleiro
Empecemos por crear una hoja muy básica, que busque el elemento raíz y devuelva como salida el elemento datos (que va a ser la nueva raíz)
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<datos>
</datos>
</xsl:template>
</xsl:stylesheet>
Si probamos dicho XSLT aplicándolo al XML original obtendremos esto:
<datos/>
No pasa nada porque se obtenga el elemento raíz vacío, el programa de transformación lo hace para ahorrar tiempo y bytes.
Paso 2. Xenerar elementos cuenta e fondo
Una vez que hemos cambiado el elemento raíz tenemos que generar dos elementos más que agrupen los elementos cuenta y los elementos fondo. Para ello, basta con escribirlos como muestra la siguiente hoja de estilo.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<datos>
<cuentas></cuentas>
<fondos></fondos>
</datos>
</xsl:template>
</xsl:stylesheet>
[Cuentas]. Paso 1. Movendo elementos cuenta
Primeiro buscamos todos os elementos cuenta e os metemos dentro de cuentas; despois resolveremos o problema dos fondos.
Para percorrer elementos precisamos un bucle for-each. A plantilla sitúase na raíz así que precisaremos o bucle itere por cada un dos elementos de listado/cuenta. Porén, o bucle meterase no elemento fillo listado e vai devolvendo cada un dos elementos cuenta que hai dentro.
Un posible bucle sería este:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<datos>
<cuentas>
<xsl:for-each select="listado/cuenta">
<cuenta></cuenta>
</xsl:for-each>
</cuentas>
<fondos></fondos>
</datos>
</xsl:template>
</xsl:stylesheet>
Cos nosos datos dará isto como resultado:
<datos>
<cuentas>
<cuenta/>
<cuenta/>
</cuentas>
<fondos/>
</datos>
A plantilla xenera dous elementos cuenta, un por cada conta que nos da o bucle. Poderíamos facer isto para ter un nome de elemento distinto. Este será o xeito para poder cambiar de nome un elemento:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<datos>
<cuentas>
<xsl:for-each select="listado/cuenta">
<otroelemento></otroelemento>
</xsl:for-each>
</cuentas>
<fondos></fondos>
</datos>
</xsl:template>
</xsl:stylesheet>
[Cuentas]. Paso 2. Meter dentro de atributo dnititular
Sigamos con el problema original: ya hemos creado un elemento cuentas que lleva dentro un elemento cuenta para cada una de las cuentas originales. Ahora en dicho elemento cuenta vamos a meter dentro un atributo llamado dnititular usando la etiqueta xsl:attribute que debe ir dentro del elemento al que le queramos poner el atributo y además al principio. Si queremos varios atributos no pasa nada podemos ponerlos todos dentro del elemento pero recordando ponerlos al principio.
Así, el código siguiente nos fabrica el atributo.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<datos>
<cuentas>
<xsl:for-each select="listado/cuenta">
<cuenta>
<!--Esto añade el atributo dnititular a cuenta-->
<xsl:attribute name="dnititular">10</xsl:attribute>
</cuenta>
</xsl:for-each>
</cuentas>
<fondos></fondos>
</datos>
</xsl:template>
</xsl:stylesheet>
[Cuentas]. Paso 3. Extraer valor do atributuo dni
Pero hay un problema, todas las cuentas tienen el dnititular a 10. Necesitamos la etiqueta value-of que nos permite extraer el contenido de un elemento o atributo, en este caso queremos extrar el valor del atributo dni que está dentro del elemento titular. Esto se hace con titular/@dni que significa «extraer el atributo dni que debe estar dentro del elemento titular».
Así, el código siguiente:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<datos>
<cuentas>
<xsl:for-each select="listado/cuenta">
<cuenta>
<xsl:attribute name="dnititular">
<xsl:value-of select="titular/@dni"/>
</xsl:attribute>
</cuenta>
</xsl:for-each>
</cuentas>
<fondos></fondos>
</datos>
</xsl:template>
</xsl:stylesheet>
Devolve coma resultado:
<datos>
<cuentas>
<cuenta dnititular="5671001D"/>
<cuenta dnititular="39812341C"/>
</cuentas>
<fondos/>
</datos>
[Cuentas]. Paso 4. Crear elemento titular
El paso siguiente va a ser crear el elemento titular que también se llama titular en el archivo original. Para ello lo metemos dentro de cuenta y permitiendo que la creación del atributo dnititular se quede al principio.
El código XSLT es este
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<datos>
<cuentas>
<xsl:for-each select="listado/cuenta">
<cuenta>
<xsl:attribute name="dnititular">
<xsl:value-of select="titular/@dni"/>
</xsl:attribute>
<!--Creamos el elemento titular y metemos
dentro del valor original del titular-->
<titular>
<xsl:value-of select="titular"/>
</titular>
</cuenta>
</xsl:for-each>
</cuentas>
<fondos></fondos>
</datos>
</xsl:template>
</xsl:stylesheet>
Que xenera o seguinte resultado:
<datos>
<cuentas>
<cuenta dnititular="5671001D">
<titular>Ramon Perez</titular>
</cuenta>
<cuenta dnititular="39812341C">
<titular>Carmen Diaz</titular>
</cuenta>
</cuentas>
<fondos/>
</datos>
[Cuentas]. Paso 5. Crear elemento saldoactual
Ahora vamos a crear el elemento saldoactual. Dentro de ese elemento debemos escribir el texto que ya tuviese el saldoactual antiguo y escribiendo al lado el atributo moneda.
O código necesario é:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<datos>
<cuentas>
<xsl:for-each select="listado/cuenta">
<cuenta>
<xsl:attribute name="dnititular">
<xsl:value-of select="titular/@dni"/>
</xsl:attribute>
<!--Creamos el elemento titular y metemos
dentro del valor original del titular-->
<titular>
<xsl:value-of select="titular"/>
</titular>
<!--Creamos el saldo actual-->
<saldoactual>
<!--Y metemos dentro la cantidad que tuviese
el fichero original...-->
<xsl:value-of select="saldoactual"/>
<!--Y extraemos la moneda...-->
<xsl:value-of select="saldoactual/@moneda"/>
</saldoactual>
</cuenta>
</xsl:for-each>
</cuentas>
<fondos></fondos>
</datos>
</xsl:template>
</xsl:stylesheet>
O resultado é este:
<datos>
<cuentas>
<cuenta dnititular="5671001D">
<titular>Ramon Perez</titular>
<saldoactual>12000euros</saldoactual>
</cuenta>
<cuenta dnititular="39812341C">
<titular>Carmen Diaz</titular>
<saldoactual>1900euros</saldoactual>
</cuenta>
</cuentas>
<fondos/>
</datos>
[Cuentas]. Paso 7. Engadimos data de creación
Por último, engadimos a data de creación. No ficheiro orixinal chámase:
- fechacreación
E no ficheiro final chámase creación e vai coma primeiro elemento.
O XSLT sería este:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<datos>
<cuentas>
<xsl:for-each select="listado/cuenta">
<cuenta>
<xsl:attribute name="dnititular">
<xsl:value-of select="titular/@dni"/>
</xsl:attribute>
<!--Creamos el elemento "creación" que en
realidad contiene el texto del elemento
"fechacreación" original-->
<creacion>
<xsl:value-of select="fechacreacion"/>
</creacion>
<!--Creamos el elemento titular y metemos
dentro del valor original del titular-->
<titular>
<xsl:value-of select="titular"/>
</titular>
<!--Creamos el saldo actual-->
<saldoactual>
<!--Y metemos dentro la cantidad que tuviese
el fichero original...-->
<xsl:value-of select="saldoactual"/>
<!--Y extraemos la moneda...-->
<xsl:value-of select="saldoactual/@moneda"/>
</saldoactual>
</cuenta>
</xsl:for-each>
</cuentas>
<fondos></fondos>
</datos>
</xsl:template>
</xsl:stylesheet>
Que xenera o resultado seguinte:
<datos>
<cuentas>
<cuenta dnititular="5671001D">
<creacion>13-abril-2012</creacion>
<titular>Ramon Perez</titular>
<saldoactual>12000euros</saldoactual>
</cuenta>
<cuenta dnititular="39812341C">
<creacion>15-febrero-2011</creacion>
<titular>Carmen Diaz</titular>
<saldoactual>1900euros</saldoactual>
</cuenta>
</cuentas>
<fondos/>
</datos>
Así a parte de cuentas que rematada.
[Fondo] Paso 1. Crear elemento fondo
Falta facer o seguinte
- Facer un elemento fondos cun bucle que vaia xenerando elementos fondo
- Poñer no fondo o atributo cuentaasociada
- Crearol elemento cantidaddepositada
- Crear o elemento moneda
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<datos>
<cuentas>
<xsl:for-each select="listado/cuenta">
<cuenta>
<xsl:attribute name="dnititular">
<xsl:value-of select="titular/@dni"/>
</xsl:attribute>
<!--Creamos el elemento "creación" que en
realidad contiene el texto del elemento
"fechacreación" original-->
<creacion>
<xsl:value-of select="fechacreacion"/>
</creacion>
<!--Creamos el elemento titular y metemos
dentro del valor original del titular-->
<titular>
<xsl:value-of select="titular"/>
</titular>
<!--Creamos el saldo actual-->
<saldoactual>
<!--Y metemos dentro la cantidad que tuviese
el fichero original...-->
<xsl:value-of select="saldoactual"/>
<!--Y extraemos la moneda...-->
<xsl:value-of select="saldoactual/@moneda"/>
</saldoactual>
</cuenta>
</xsl:for-each>
</cuentas>
<fondos>
<xsl:for-each select="listado/fondo">
<!--Paso 1: crear un fondo por cada fondo original-->
<fondo>
</fondo>
</xsl:for-each>
</fondos>
</datos>
</xsl:template>
</xsl:stylesheet>
[Fondo] Paso 2. Poner el atributo cuentaasociado
El XSLT sería así:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<datos>
<cuentas>
<xsl:for-each select="listado/cuenta">
<cuenta>
<xsl:attribute name="dnititular">
<xsl:value-of select="titular/@dni"/>
</xsl:attribute>
<!--Creamos el elemento "creación" que en
realidad contiene el texto del elemento
"fechacreación" original-->
<creacion>
<xsl:value-of select="fechacreacion"/>
</creacion>
<!--Creamos el elemento titular y metemos
dentro del valor original del titular-->
<titular>
<xsl:value-of select="titular"/>
</titular>
<!--Creamos el saldo actual-->
<saldoactual>
<!--Y metemos dentro la cantidad que tuviese
el fichero original...-->
<xsl:value-of select="saldoactual"/>
<!--Y extraemos la moneda...-->
<xsl:value-of select="saldoactual/@moneda"/>
</saldoactual>
</cuenta>
</xsl:for-each>
</cuentas>
<fondos>
<xsl:for-each select="listado/fondo">
<!--Paso 1: crear un fondo por cada fondo original-->
<fondo>
<!--Paso 2, crear el atributo cuentaasociada-->
<xsl:attribute name="cuentaasociada">
<xsl:value-of select="cuentaasociada"/>
</xsl:attribute>
</fondo>
</xsl:for-each>
</fondos>
</datos>
</xsl:template>
</xsl:stylesheet>
[Fondo] Paso 3. Crear el elemento cantidaddepositada
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<datos>
<cuentas>
<xsl:for-each select="listado/cuenta">
<cuenta>
<xsl:attribute name="dnititular">
<xsl:value-of select="titular/@dni"/>
</xsl:attribute>
<!--Creamos el elemento "creación" que en
realidad contiene el texto del elemento
"fechacreación" original-->
<creacion>
<xsl:value-of select="fechacreacion"/>
</creacion>
<!--Creamos el elemento titular y metemos
dentro del valor original del titular-->
<titular>
<xsl:value-of select="titular"/>
</titular>
<!--Creamos el saldo actual-->
<saldoactual>
<!--Y metemos dentro la cantidad que tuviese
el fichero original...-->
<xsl:value-of select="saldoactual"/>
<!--Y extraemos la moneda...-->
<xsl:value-of select="saldoactual/@moneda"/>
</saldoactual>
</cuenta>
</xsl:for-each>
</cuentas>
<fondos>
<xsl:for-each select="listado/fondo">
<!--Paso 1: crear un fondo por cada fondo original-->
<fondo>
<!--Paso 2, crear el atributo cuentaasociada-->
<xsl:attribute name="cuentaasociada">
<xsl:value-of select="cuentaasociada"/>
</xsl:attribute>
<!--Paso 3, crear el elemento cantidaddepositada-->
<cantidaddepositada>
<xsl:value-of select="datos/cantidaddepositada"/>
</cantidaddepositada>
</fondo>
</xsl:for-each>
</fondos>
</datos>
</xsl:template>
</xsl:stylesheet>
[Fondo] Paso 4. Crear o elemento moneda
O XSLT sería así:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<datos>
<cuentas>
<xsl:for-each select="listado/cuenta">
<cuenta>
<xsl:attribute name="dnititular">
<xsl:value-of select="titular/@dni"/>
</xsl:attribute>
<!--Creamos el elemento "creación" que en
realidad contiene el texto del elemento
"fechacreación" original-->
<creacion>
<xsl:value-of select="fechacreacion"/>
</creacion>
<!--Creamos el elemento titular y metemos
dentro del valor original del titular-->
<titular>
<xsl:value-of select="titular"/>
</titular>
<!--Creamos el saldo actual-->
<saldoactual>
<!--Y metemos dentro la cantidad que tuviese
el fichero original...-->
<xsl:value-of select="saldoactual"/>
<!--Y extraemos la moneda...-->
<xsl:value-of select="saldoactual/@moneda"/>
</saldoactual>
</cuenta>
</xsl:for-each>
</cuentas>
<fondos>
<xsl:for-each select="listado/fondo">
<!--Paso 1: crear un fondo por cada fondo original-->
<fondo>
<!--Paso 2, crear el atributo cuentaasociada-->
<xsl:attribute name="cuentaasociada">
<xsl:value-of select="cuentaasociada"/>
</xsl:attribute>
<!--Paso 3, crear el elemento cantidaddepositada-->
<cantidaddepositada>
<xsl:value-of select="datos/cantidaddepositada"/>
</cantidaddepositada>
<!--Paso 4:Crear el elemento moneda-->
<moneda>
<xsl:value-of select="datos/moneda"/>
</moneda>
</fondo>
</xsl:for-each>
</fondos>
</datos>
</xsl:template>
</xsl:stylesheet>
Solución final
XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<datos>
<cuentas>
<xsl:for-each select="listado/cuenta">
<cuenta>
<xsl:attribute name="dnititular">
<xsl:value-of select="titular/@dni"/>
</xsl:attribute>
<!--Creamos el elemento "creación" que en
realidad contiene el texto del elemento
"fechacreación" original-->
<creacion>
<xsl:value-of select="fechacreacion"/>
</creacion>
<!--Creamos el elemento titular y metemos
dentro del valor original del titular-->
<titular>
<xsl:value-of select="titular"/>
</titular>
<!--Creamos el saldo actual-->
<saldoactual>
<!--Y metemos dentro la cantidad que tuviese
el fichero original...-->
<xsl:value-of select="saldoactual"/>
<!--Y extraemos la moneda...-->
<xsl:value-of select="saldoactual/@moneda"/>
</saldoactual>
</cuenta>
</xsl:for-each>
</cuentas>
<fondos>
<xsl:for-each select="listado/fondo">
<!--Paso 1: crear un fondo por cada fondo original-->
<fondo>
<!--Paso 2, crear el atributo cuentaasociada-->
<xsl:attribute name="cuentaasociada">
<xsl:value-of select="cuentaasociada"/>
</xsl:attribute>
<!--Paso 3, crear el elemento cantidaddepositada-->
<cantidaddepositada>
<xsl:value-of select="datos/cantidaddepositada"/>
</cantidaddepositada>
<!--Paso 4:Crear el elemento moneda-->
<moneda>
<xsl:value-of select="datos/moneda"/>
</moneda>
</fondo>
</xsl:for-each>
</fondos>
</datos>
</xsl:template>
</xsl:stylesheet>
Referencias
- Ejercicios sobre XSLT. Oscar Gomez.