Observaciones meteorológicas: XPath, XQuery, XSLT a CSV y DTD
Siete apartados sobre los datos de una red de estaciones meteorológicas en XML: búsquedas XPath con números y atributos, medias y el día más extremo con XQuery, una exportación a CSV con XSLT de salida de texto y la DTD del documento con atributos ID y obligatorios.
- XPath con comparaciones numéricas
- distinct-values
- XQuery: let, order by y [1]
- Navegar hacia el padre (..)
- XSLT con salida de texto
- DTD: ELEMENT y ATTLIST
Enunciado
Varios institutos tienen una estación meteorológica en el tejado y comparten sus datos en un documento XML común: una estacion por instituto, con sus atributos (identificador, nombre, provincia y altitud), y dentro un dia por cada día con la temperatura máxima, la mínima y la lluvia en milímetros.
El documento declara su DTD en el DOCTYPE, pero esa DTD todavía no existe: el último apartado es escribirla. Antes, vas a responder con XPath y XQuery a las preguntas que se hace cualquiera con estos datos (dónde ha llovido más, qué estación es más fría) y a exportarlos en CSV con XSLT para abrirlos en una hoja de cálculo.
Qué hay que hacer
- Resuelve cada apartado con la herramienta que indica, sobre el documento
observaciones.xml. - Cada apartado se corrige por separado. Las consultas XPath y XQuery se comparan con el resultado de la solución sobre el documento (importan los valores y su orden); las transformaciones XSLT, con la salida esperada (sin contar espacios y saltos de línea); y los esquemas, con documentos que deben aceptar y otros que deben rechazar.
- Cuando un apartado pide «el texto» de algo, la consulta debe devolver cadenas (con
text()ostring()), no elementos.
El documento: observaciones.xml
Todos los apartados trabajan sobre este documento. Léelo entero antes de empezar: la estructura (qué es elemento y qué es atributo) decide cómo se escribe cada expresión.
1<?xml version="1.0" encoding="UTF-8"?>
2<!DOCTYPE observaciones SYSTEM "observaciones.dtd">
3<observaciones red="Estaciones de los institutos" periodo="2026-09">
4 <estacion id="E01" nombre="Madrid Retiro" provincia="Madrid" altitud="667">
5 <dia fecha="2026-09-01"><tmax>31.2</tmax><tmin>18.4</tmin><lluvia>0</lluvia></dia>
6 <dia fecha="2026-09-02"><tmax>29.8</tmax><tmin>17.9</tmin><lluvia>0</lluvia></dia>
7 <dia fecha="2026-09-03"><tmax>24.1</tmax><tmin>15.2</tmin><lluvia>12.4</lluvia></dia>
8 <dia fecha="2026-09-04"><tmax>26.5</tmax><tmin>14.8</tmin><lluvia>1.2</lluvia></dia>
9 </estacion>
10 <estacion id="E02" nombre="Navacerrada" provincia="Madrid" altitud="1894">
11 <dia fecha="2026-09-01"><tmax>19.6</tmax><tmin>9.1</tmin><lluvia>0</lluvia></dia>
12 <dia fecha="2026-09-02"><tmax>18.2</tmax><tmin>8.4</tmin><lluvia>3.5</lluvia></dia>
13 <dia fecha="2026-09-03"><tmax>12.7</tmax><tmin>5.9</tmin><lluvia>28.1</lluvia></dia>
14 <dia fecha="2026-09-04"><tmax>15.0</tmax><tmin>6.3</tmin><lluvia>6.0</lluvia></dia>
15 </estacion>
16 <estacion id="E03" nombre="Valencia Puerto" provincia="Valencia" altitud="5">
17 <dia fecha="2026-09-01"><tmax>30.1</tmax><tmin>23.5</tmin><lluvia>0</lluvia></dia>
18 <dia fecha="2026-09-02"><tmax>30.6</tmax><tmin>24.0</tmin><lluvia>0</lluvia></dia>
19 <dia fecha="2026-09-03"><tmax>28.4</tmax><tmin>22.1</tmin><lluvia>0.4</lluvia></dia>
20 <dia fecha="2026-09-04"><tmax>27.9</tmax><tmin>21.7</tmin><lluvia>15.8</lluvia></dia>
21 </estacion>
22</observaciones>Guía paso a paso
Intenta resolverlo por tu cuenta y abre un paso solo cuando te atasques: cada uno te acerca a la solución sin dártela entera.
1. Números en XPath
En una comparación con un número, XPath convierte el atributo o el elemento a número: [@altitud > 500] y [lluvia > 10] funcionan sin conversiones. max, min, avg y sum también trabajan con números.
2. Subir al padre
Desde un dia, .. es su estacion: $d/../@nombre da el nombre de la estación de ese día. Es lo que permite recorrer los días y seguir sabiendo de qué estación son.
3. Quedarse con el primero en XQuery
Un FLWOR devuelve una secuencia. Para «el mayor», ordénalo de mayor a menor y toma el primero con un predicado sobre la secuencia entera entre paréntesis.
(for $d in //dia
order by $d/tmax descending
return string($d/@fecha))[1]4. XSLT que genera texto
Con <xsl:output method="text"/> no se generan etiquetas: solo el texto de los xsl:value-of y xsl:text. Los espacios y saltos de línea de la hoja no salen, así que los saltos hay que escribirlos con .
5. Una DTD de arriba abajo
Declara cada elemento con su contenido ((hijos), (#PCDATA)) y, a continuación, sus atributos con ATTLIST. Comprueba con los documentos del ejercicio: cada documento inválido rompe una sola regla.
Resuélvelo aquí
Cada apartado se corrige por separado: las consultas se comparan con el resultado esperado sobre el documento de arriba, las transformaciones con la salida esperada y los esquemas con documentos que deben aceptar o rechazar.
El nombre (el valor del atributo, como texto) de las estaciones situadas a más de 500 metros de altitud, en el orden del documento.
La temperatura máxima más alta registrada en todo el periodo (un número).
Las fechas (sin repetir y en el orden del documento) en las que alguna estación registró más de 10 mm de lluvia.
Para cada estación, en el orden del documento, una cadena nombre: media de máximas X °C, lluvia total Y mm, con la media redondeada a un decimal.
El día de mayor amplitud térmica (máxima menos mínima) de toda la red, como una única cadena estación (fecha): amplitud X °C con la amplitud redondeada a un decimal.
Exporta a CSV (salida de texto) los días con lluvia: una primera línea estacion;fecha;tmax;tmin;lluvia y una línea por cada día con más de 0 mm, con el nombre de la estación, ordenadas por nombre de estación y después por fecha. Cada línea termina con un salto de línea.
Escribe la DTD observaciones.dtd: observaciones con uno o más estacion y los atributos obligatorios red y periodo; estacion con uno o más dia y los atributos obligatorios id (de tipo ID, único en el documento), nombre, provincia y altitud; dia con tmax, tmin y lluvia en ese orden y el atributo obligatorio fecha; y tmax, tmin y lluvia con texto.
Documentos de prueba (pulsa para verlos):
Solución explicada
Ver las soluciones de todos los apartados
Apartado 1 · XPath
/observaciones/estacion[@altitud > 500]/@nombre/string()Apartado 2 · XPath
max(//tmax)Apartado 3 · XPath
distinct-values(//dia[lluvia > 10]/@fecha)Apartado 4 · XQuery
for $e in /observaciones/estacion
return concat($e/@nombre, ': media de máximas ', round(avg($e/dia/tmax) * 10) div 10, ' °C, lluvia total ', sum($e/dia/lluvia), ' mm')Apartado 5 · XQuery
(for $d in //dia
let $amplitud := $d/tmax - $d/tmin
order by $amplitud descending
return concat($d/../@nombre, ' (', $d/@fecha, '): amplitud ', round($amplitud * 10) div 10, ' °C'))[1]Apartado 6 · XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/observaciones">
<xsl:text>estacion;fecha;tmax;tmin;lluvia </xsl:text>
<xsl:for-each select="estacion/dia[lluvia > 0]">
<xsl:sort select="../@nombre"/>
<xsl:sort select="@fecha"/>
<xsl:value-of select="concat(../@nombre, ';', @fecha, ';', tmax, ';', tmin, ';', lluvia)"/>
<xsl:text> </xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>Apartado 7 · DTD
<!ELEMENT observaciones (estacion+)>
<!ATTLIST observaciones
red CDATA #REQUIRED
periodo CDATA #REQUIRED>
<!ELEMENT estacion (dia+)>
<!ATTLIST estacion
id ID #REQUIRED
nombre CDATA #REQUIRED
provincia CDATA #REQUIRED
altitud CDATA #REQUIRED>
<!ELEMENT dia (tmax, tmin, lluvia)>
<!ATTLIST dia fecha CDATA #REQUIRED>
<!ELEMENT tmax (#PCDATA)>
<!ELEMENT tmin (#PCDATA)>
<!ELEMENT lluvia (#PCDATA)>Las consultas XPath se apoyan en la conversión automática a número: comparar un atributo con 500 o un elemento con 10 funciona directamente, y las funciones de agregado (max, avg, sum) hacen el resto.
El eje padre (..) es lo que distingue a XPath de una búsqueda de texto: desde un día se llega a su estación sin guardar nada aparte. En XQuery, guardar la amplitud en un let permite ordenar por ella y usarla en el resultado.
La salida de texto de XSLT convierte XML en cualquier formato plano (CSV, ficheros de configuración, SQL). Ordenar por dos claves con dos xsl:sort funciona igual que un ORDER BY con dos columnas.
La DTD es más limitada que el XML Schema (no tiene tipos de datos: una altitud negativa sería válida), pero describe la estructura y garantiza identificadores únicos con ID. Por eso sigue apareciendo en documentos antiguos y en el DOCTYPE de HTML.
Para ir más allá
- Escribe el XML Schema equivalente a la DTD, con tipos numéricos y la fecha como
xs:date. - Genera con XSLT una página HTML con una tabla por estación y las celdas de lluvia coloreadas según la cantidad.
- Calcula con XQuery el día más lluvioso de cada provincia agrupando con
distinct-values(@provincia).