Introducción a los Servicios Web en Java (II): XML y Java
XML es el lenguaje de los Servicios Web. Cuando invocamos un servicio u obtenemos una respuesta desde el mismo, toda la información que estamos enviando/recibiendo es en formato XML. Tanto la estructura de datos de un servicio como su interface se definen con XML. XML está presente en todas partes cuando hablamos de Servicios Web.
En este segundo artículo del tutorial de Servicios Web en Java vamos a ver las bases de XML desde una perspectiva teórica, para a continuación adentrarnos en la parte práctica a través de las API's StAX y XPath.
XML (eXtensible Markup Language, Lenguaje de Marcado Extensible) es como su nombre indica, un lenguaje de marcado: un lenguaje para codificar un documento mediante marcas (etiquetas), las cuales contienen información acerca de la estructura, contenido y/o presentación de dicho documento. HTML es otro lenguaje de marcado, aunque éste fue diseñado con la intención de presentar información (mientras que la misión de XML es la de estructurar, transportar y almacenar información). Un documento XML se compone como mínimo de una declaración, un elemento raíz (también llamado elemento padre) y uno o más sub-elementos (también llamados elementos hijo), que a su vez pueden anidar otros sub-elementos: La primera línea es la declaración XML, y en ella se define tanto la versión de XML como la codificación que vamos a usar en el documento. En los próximos ejemplos se omitirá para simplificar el código, pero en el mundo real debes usarla siempre. El resto del documento contiene la información en si. A la estructura de un documento XML se la llama árbol XML ya que podemos considerar el elemento padre Exceptuando la declaración XML, todos los elementos deben ser cerrados mediante su etiqueta de cierre ( XML permite incluir comentarios que, como en otros lenguajes, son omitidos durante el proceso de parseado. Pueden ir en cualquier parte del documento y pueden ser multilínea: Por último, existen cinco caracteres que requieren un tratamiento especial cuando queremos usarlos de forma literal. Dicho tratamiento consiste en sustituirlos por su entidad de referencia asociada: Usados de forma literal el parseador puede tratarlos de forma incorrecta, provocando que se malforme el documento y se produzca un más que probable error. Veamos un ejemplo: El contenido de la etiqueta <regla> contiene el carácter Puesto que prácticamente todo en un documento XML está formado por elementos, vamos a ver en detalle este componente. Un elemento puede contener lo siguiente: El único punto que no hemos visto aún es el de Atributos, aunque su nombre deja muy clara su función: Básicamente, el atributo El nombre de un elemento puede ser, como hemos visto hasta ahora, cualquiera que se te ocurra (incluyendo letras, números y caracteres especiales), aunque existen ciertas restricciones que debemos cumplir: Además, existen ciertas convenciones que es útil seguir a la hora de elegir el nombre de un elemento: Como su nombre indica, XML es extensible. ¿Y que significa esto? Ni más ni menos que podemos extender un documento (añadir información) sin romper su estructura: Todo el software que procese información sobre la versión anterior de la etiqueta Los namespaces (nombres de espacio) proveen un método para la organización jerárquica de la información contenida en distintos documentos XML, y así evitar conflictos entre elementos con un mismo nombre. Veamos un ejemplo: En el ejemplo anterior tenemos un elemento con el mismo nombre que está siendo usado en dos representaciones de información distintas: La forma de solucionar este problema es haciendo uso de prefijos: En el ejemplo anterior hemos definido todos los elementos mediante los prefijos Como puedes ver en el ejemplo anterior, usamos el atributo xmlns (XML NameSpace) para definir una URI que identifica y asocia el prefijo usado con nuestro namespace. La URI no tiene porqué ser accesible en internet (de hecho no lo es), su única misión es identificar un namespace (imagínalo como la sentencia Otra forma de declarar un namespace es haciéndolo en el elemento raíz del documento: La aplicación y uso de namespaces es algo más profunda que lo expuesto hasta ahora, permitiendo definir comportamientos como redefinición y sobreescritura, por ejemplo. Sin embargo características tan avanzadas (que realmente no lo son) no son necesarias para seguir este tutorial. Con esto ya tenemos la base de conocimientos sobre XML necesaria para comenzar a jugar con el lenguaje. Ahora es el momento de introducir la API StAX. Asumiendo que XML está en todas partes cuando hablamos de Servicios Web, tarde o temprano se hace necesario manipular documentos XML a través de nuestro querido lenguaje Java. Aunque existen mecanismos para poner en marcha Servicios Web mediante tecnologías como RMI o EJB, éstas sólo representan una capa de abstracción sobre el código XML subyacente. StAX (Streaming API for XML, API para la transmisión de XML) es una API para la manipulación de documentos XML que surgió para cubrir las carencias que sufrían las APIs SAX y DOM, situándose en un punto intermedio a nivel de procesamiento entre estas dos, convirtiéndola en una API más eficiente. Mientras que en DOM accedemos al árbol completo del documento XML (lo que nos permite un acceso aleatorio a cualquier parte de este pero a un coste elevado), en SAX se registran eventos asociados con cada elemento, de manera que cuando necesitamos acceder a uno de ellos debemos empujar la información de dicho elemento hacia nuestra aplicación (más eficiente). En StaX resolvemos el problema del acceso a los datos de un documento XML desde una perspectiva intermedia: usando un cursor nos movemos a través del árbol según sea necesario, y sólo cuando estamos en el nodo adecuado tiramos de la información. Es un modelo más cercano a SAX (eventos) que a DOM (árbol), pero al contrario que con el primero no se mantiene un registro actualizado de todos los eventos, si no que gracias al cursor vamos descubriendo los elementos y sus eventos asociados en tiempo real. De igual forma, mientras nos movemos vamos olvidando elementos/eventos pasados. De manera adicional, StAX permite escribir documentos XML (SAX por ejemplo sólo permite lectura), de manera que además de un tratamiento de datos más efectivo, reducimos el número de APIs que son necesarias cuando se requiere una gestión integral. Por todos estos motivos, StAX va a ser nuestra API de referencia (salvo honrosas excepciones) cuando manejemos documentos XML desde Java. Una de las más sencillas pero profundas frases que Carl Sagan nos dejó fue la siguiente: En nuestro caso, para poder parsear XML mediante StAX lo primero que necesitamos es, un fichero XML: Ahora que tenemos nuestro universo, ya podemos ponernos el delantal y comenzar a parsear XML mediante StAX. Como puedes observar, el documento XML contiene información sobre un párking con plazas (de aparcamiento) que contienen información sobre un vehículo y su propietario. Lo primero que vamos a hacer es obtener e imprimir por consola el número de plazas: No es necesario explicar con detalle cada función de cada método de cada API, eso es algo que está totalmente fuera de lugar; toda esa información puedes encontrarla en la documentación oficial. Lo que realmente nos interesa es el código donde realizamos el tratamiento del documento XML. Una vez que hemos creado el objeto Todos los eventos, independientemente de su tipo, tienen unas propiedades genéricas que podemos invocar:
Sin embargo, en ciertas ocasiones no necesitamos conocer las propiedades del evento, si no el tipo mismo del evento: Obtener el tipo de evento es tremendamente útil cuando queremos realizar distintas acciones dependiendo del tipo de evento: Para finalizar con la lectura de información vamos a escribir un sencillo ejemplo para mostrar por consola todas las matrículas y su identificador de plaza asociado: Puesto que StAX trabaja con un modelo de cursor, es necesario usar una variable temporal cuando necesitamos gestionar múltiples eventos como si fueran uno solo. Para resolver este problema hubiera sido más conveniente usar DOM, pero uno de los objetivos de este artículo es mostrar StAX y por tanto hemos resuelto nuestro problema mediante éste. Sin embargo, en el mundo real nunca debes pensar en términos de preferencia, si no de conveniencia. Antes de finalizar con esta sección te lanzo dos retos: Ahora que ya sabemos como leer un documento XML, es el momento de crearlos desde cero. Y de nuevo, StAX al rescate: El código anterior va a generar un documento similar al que leíamos en la sección anterior (aunque para mantener el código lo más reducido posible hemos generado solamente el primer elemento El ejemplo anterior no requiere apenas explicación: las clases de lectura InputXxx y XxxReader han sido sustituidas por clases OutputXxx y XxxWriter, y la construcción de los elementos XML es realmente sencilla e intuitiva. El único punto de interés proviene del uso de XPath (XML Path, Rutas XML) es una librería incluida en Java para la evaluación de expresiones sobre documentos XML, y al mismo tiempo es el lenguaje que define dichas expresiones y que depende del W3C Consortium (y por tanto está asociado a la propia red Intenet y no a ningún lenguaje de programación en concreto). A efectos prácticos, XPath es ambas cosas cuando trabajamos con Java. En XPath se evalúa un documento XML como si fuera una estructura de tipo árbol. Cada elemento XML se denomina nodo (cuando existe anidamiento hablamos de subnodos, pero no dejan de ser nodos en si mismos), y el camino que debemos seguir para alcanzar un nodo en concreto se denomina ruta (de ahí el nombre de la librería): Extrapolándolo a nuestro documento XML de plazas de párking: La expresión anterior va a alcanzar a todos los elementos <plaza>. Ahora imaginemos que queremos ser más selectivos y alcanzar sólo las matrículas: Pero XPath es aún más potente: con la siguiente expresión podemos filtrar todas las plazas cuya fecha de vencimiento cumpla en una fecha concreta: Si queremos filtrar aún más la información obtenida, podemos indicar que una vez realizado el filtro por fecha de vencimiento sólo nos devuelva el nombre de los propietarios: También podemos seleccionar un nodo por índice, esto es, la posición dentro del árbol XML (ten presente que se tratan de indices basados en uno, al contrario que cuando trabajamos con arrays e indices basados en cero): Incluso podemos subir a través de los nodos padre una vez que hemos realizado el filtrado. ¿Recuerdas el fragmento XML de la sección 2.4? Con la siguiente expresión alcanzamos el número de empleado de todos aquellos con formación en tecnología Java: Y con la siguiente expresión obtenemos la información completa de los empleados que, además de tener formación en tecnología Java, tienen un nivel de conocimientos alto: XPath define toda una serie que selectores ( Ahora que sabemos como construir expresiones es el momento de ejecutarlas: En el ejemplo anterior hemos usado DOM para recrear el árbol XML y XPath para evaluar una expresión. El resultado final es una lista de nodos ( Al igual que cuando trabajamos con SQL, las expresiones XPath deben ser validadas para evitar ataques de XPath Injection. Imagina el siguiente método para obtener los datos de una plaza por número de matrícula: Si llamamos a nuestro flamante método pasándole como parámetro una matrícula existente, nos devolverá la información deseada. Si la matrícula no existe, devolverá una lista vacía. ¿Pero que pasa si el parámetro El resultado devuelto al pasar la cadena anterior como valor del parámetro ¿Qué ha ocurrido? Muy sencillo: al no validar la expresión, nuestro no tan flamante método nos da la posibilidad de construir expresiones complejas. En nuestro caso hemos construido una expresión condicional con tres condiciones separadas por dos operadores La primera y tercera condiciones devuelven La solución requiere validar la entrada de datos: Una solución más acorde con el mundo real sería validar la matrícula contra una expresión regular apropiada, y sólo en caso de validación positiva ejecutar la llamada. En el caso de parámetros numéricos la solución es mucho más sencilla, ya que una llamada a En este artículo del tutorial de Servicios Web en Java hemos visto tanto el lenguaje XML como algunas herramientas Java para trabajar con él. Apenas hemos empezado con XML, y a lo largo de todo el tutorial haremos un uso intensivo de este lenguaje, además de presentar más herramientas Java para la manipulación de XML (como JAXB). En el próximo artículo veremos XML Schema, el lenguaje para describir la estructura, constricciones y restricciones de un documento XML. También veremos algunos patrones de diseño y buenas prácticas para diseñar tipos de datos listos para ser usados en nuestros Servicios Web.2.1 XML: lenguaje de marcado
2.2 XML: estructura y sintaxis
<parking>
como la raíz, cada uno de los elementos hijo <plaza>
como una rama, y cada uno de los elementos hijo <matrícula>
, <marca>
y <modelo>
como una rama que nace de la rama anterior.</parking>
, </plaza>
, etc). El nombre de todas las etiquetas (ya sean de apertura o cierre) es sensible a mayúsculas, por lo que <matricula>
es distinto de <Matricula>
. El orden de apertura-cierre-anidación debe ser el correcto, o el documento estará mal formado y no será válido.
Carácter especial
Entidad de referencia
&
&
<
<
>
>
"
"
'
'
<!-- XML inválido -->
<apunte lenguaje="java">
<regla>Usamos el carácter
>
para comparar si un valor numérico primitivo es mayor que otro.</regla>
<excepcion>Cuando trabajamos con objetos wrapper (Integer, Float) es preferible usar el método compare()</excepcion>
<apunte>
>
, y el parseador puede interpretarlo como un carácter de marcado (carácter de cierre de etiqueta) en lugar de como un carácter de contenido (símbolo mayor qué). La solución es usar la entidad de referencia asociada al carácter conflictivo:
<!-- XML válido -->
<apunte lenguaje="java">
<regla>Usamos el carácter
>
para comparar si un valor numérico primitivo es mayor que otro.</regla>
<excepcion>Cuando trabajamos con objetos wrapper (Integer, Float) es preferible usar el método compare()</excepcion>
<apunte>
2.3 XML: elementos en detalle
departamento
ofrece información adicional sobre el elemento <empleado>
. Los atributos ser usados para proveer información que es irrelevante para el uso que queremos hacer de los datos, pero puede ser útil para, por ejemplo, su tratamiento (como realizar un filtrado). Ten en cuenta que podríamos sustituir el atributo por un elemento hijo, y estaríamos proveyendo la misma información; usa el estilo que más te convenga, aunque cuando no tengas clara la relevancia real es aconsejable, por legibilidad, usar un elemento hijo:
xml
ni cualquier combinación de ésta que combine mayúsculas y minúsculas
<matricula>
que <numero_de_matricula>
2.4 XML: extensible
<empleado>
seguirá funcionando después de la extensión, puesto que la estructura original sigue presente (los elementos afectados mantienen el nombre y nivel de anidamiento).2.5 XML: namespaces
<nombre>
. A la hora de hacer referencia a este elemento nos vamos a encontrar con un problema de ambigüedad que va a derivar en un conflicto de nombres.
d
y e
para constatar a que namespace corresponde cada uno de ellos. Ahora sólo nos falta declarar los namespaces correspondientes:
package
en Java).
2.6 StAX: conceptos básicos
2.7 StAX: leyendo XML
Si quieres hacer un pastel de manzana desde el principio, primero debes crear el Universo.
package es.davidmarco.soa.stax;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
public class Lector {
// La ruta del documento XML, al ser una ruta relativa suele ser el directorio
// principal del proyecto (p.e. /home/usuario/eclipse/workspace/proyecto)
public static final String DOCUMENTO_XML = "parking.xml";
public static final String ELEMENTO_PLAZA = "plaza";
public int numeroDePlazas() {
int numeroPlazas = 0;
XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory();
InputStream inputStream = null;
XMLStreamReader xmlStreamReader = null;
try {
inputStream = new FileInputStream(DOCUMENTO_XML);
xmlStreamReader = xmlInputFactory.createXMLStreamReader(inputStream);
while (xmlStreamReader.hasNext()) {
xmlStreamReader.next();
if (xmlStreamReader.isStartElement() && ELEMENTO_PLAZA.equals(xmlStreamReader.getLocalName())) {
numeroPlazas++;
}
}
} catch (XMLStreamException ex) {
ex.printStackTrace();
} catch (FileNotFoundException ex) {
ex.printStackTrace();
} finally {
try {
xmlStreamReader.close();
inputStream.close();
} catch (XMLStreamException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
}
}
return numeroPlazas;
}
public static void main(String[] args) {
Lector lector = new Lector();
System.out.println("Número de plazas: " + lector.numeroDePlazas());
}
}
XMLStreamReader
llamamos a su método hasNext()
para comprobar si existe un evento en la siguiente posición del cursor. En caso afirmativo, nos movemos a dicha posición invocando al método next()
. En este momento, y mientras no movamos de nuevo el cursor, todas las operaciones que realicemos a través de la instancia xmlStreamReader
se realizarán sobre el evento actual. En nuestro caso hemos realizado dos operaciones: isStartElement()
para comprobar si el evento actual es una apertura de etiqueta XML y getLocalName()
para comprobar si su nombre es plaza. En caso afirmativo incrementamos en uno nuestro contador de plazas. Al finalizar la ejecución obtenemos el siguiente resultado por consola:
Número de plazas: 3
xmlStreamReader.isStartElement()
while (xmlStreamReader.hasNext()) {
xmlStreamReader.next();
int tipoEvento = xmlStreamReader.getEventType();
if ((tipoEvento == XMLEvent.START_ELEMENT) && (ELEMENTO_PLAZA.equals(xmlStreamReader.getLocalName()))) {
numeroPlazas++;
}
}
switch (tipoEvento) {
case XMLEvent.START_ELEMENT:
// acción 1
break;
case XMLEvent.END_ELEMENT:
// acción 2
break;
case XMLEvent.NAMESPACE:
// acción 3
break;
default:
// acción por defecto
break;
}
package es.davidmarco.soa.stax;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
public class Lector {
public static final String DOCUMENTO_XML = "parking.xml";
public static final String ELEMENTO_PLAZA = "plaza";
public static final String ELEMENTO_MATRICULA = "matricula";
public static final String ATRIBUTO_ID = "id";
public List<String> matriculasConId() {
List<String> matriculasConIdList = new ArrayList<String>();
XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory();
InputStream inputStream = null;
XMLStreamReader xmlStreamReader = null;
try {
inputStream = new FileInputStream(DOCUMENTO_XML);
xmlStreamReader = xmlInputFactory.createXMLStreamReader(inputStream);
String tmpMatriculaId = null;
while (xmlStreamReader.hasNext()) {
xmlStreamReader.next();
if (xmlStreamReader.isStartElement() && ELEMENTO_PLAZA.equals(xmlStreamReader.getLocalName())) {
// Obtenemos el valor del primer atributo del elemento (id) (numeración basada en indice-cero)
tmpMatriculaId = xmlStreamReader.getAttributeValue(0);
// En esta iteración es imposible que la siguiente condición se cumpla, por lo que saltamos a la siguiente
continue;
}
if (xmlStreamReader.isStartElement() && ELEMENTO_MATRICULA.equals(xmlStreamReader.getLocalName())) {
// Obtenemos el texto contenido dentro del elemento (entre las etiquetas de apertura y cierre)
tmpMatriculaId += ":" + xmlStreamReader.getElementText();
matriculasConIdList.add(tmpMatriculaId);
// Reseteamos la cadena temporal de id:matricula
tmpMatriculaId = null;
}
}
} catch (XMLStreamException ex) {
ex.printStackTrace();
} catch (FileNotFoundException ex) {
ex.printStackTrace();
} finally {
try {
xmlStreamReader.close();
inputStream.close();
} catch (XMLStreamException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
}
}
return matriculasConIdList;
}
public static void main(String[] args) {
Lector lector = new Lector();
System.out.println("Matrículas con id: " + lector.matriculasConId());
}
}
if
por un único bloque switch
?id
por nombre en lugar de por posición?2.8 StAX: escribiendo XML
package es.davidmarco.soa.stax;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
public class Escritor {
public void generarParkingXML() {
XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newFactory();
FileOutputStream fileOutputStream = null;
XMLStreamWriter xmlStreamWriter = null;
try {
fileOutputStream = new FileOutputStream("/parking.xml");
xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(fileOutputStream);
// Decorador para la indentación del documento
xmlStreamWriter = new IndentingXMLStreamWriter(xmlStreamWriter);
// Encoding y versión de XML
xmlStreamWriter.writeStartDocument("UTF-8", "1.0");
// Comentario XML
xmlStreamWriter.writeComment(" Generado mediante StAX ");
// Abrimos <parking> (elemento root)
xmlStreamWriter.writeStartElement("parking");
// Abrimos <plaza>
xmlStreamWriter.writeStartElement("plaza");
xmlStreamWriter.writeAttribute("id", "1");
xmlStreamWriter.writeStartElement("matricula");
xmlStreamWriter.writeCharacters("AAA-1111");
xmlStreamWriter.writeEndElement();
xmlStreamWriter.writeStartElement("propietario");
xmlStreamWriter.writeCharacters("Juan Sánchez");
xmlStreamWriter.writeEndElement();
xmlStreamWriter.writeStartElement("telefono");
xmlStreamWriter.writeCharacters("111-88-88");
xmlStreamWriter.writeEndElement();
xmlStreamWriter.writeStartElement("vencimiento");
xmlStreamWriter.writeCharacters("04-12-2014");
xmlStreamWriter.writeEndElement();
// Cerramos <plaza>
xmlStreamWriter.writeEndElement();
// Cerramos <parking> (elemento root)
xmlStreamWriter.writeEndDocument();
} catch (FileNotFoundException ex) {
ex.printStackTrace();
} catch (XMLStreamException ex) {
ex.printStackTrace();
} finally {
try {
xmlStreamWriter.flush();
xmlStreamWriter.close();
fileOutputStream.close();
} catch (XMLStreamException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
public static void main(String[] args) {
Escritor escritor = new Escritor();
escritor.generarParkingXML();
}
}
<plaza>
). El resultado es el siguiente:
IndentingXMLStreamWriter
, una clase que actúa como decorador de XMLStreamWriter
y que indenta automáticamente todo el código generado.2.9 XPath: conceptos básicos
2.10 XPath: construyendo expresiones
/raiz/nodo
/parking/plaza
/parking/plaza/matricula
/parking/plaza[vencimiento = '04-12-2014']
/parking/plaza[vencimiento = '04-12-2014']/propietario
/parking/plaza[1]
/empleado/tecnologias/tecnologia[nombre = 'Java']/../../numero
/empleado/tecnologias/tecnologia[nombre = 'Java' and nivel = 'Alto']
/
, ..
, @
, etc) y operadores (and
, or
, >
, ≥
, <
, ≤
, etc) para realizar estas y otras operaciones. Te recomiento que visites el manual de referencia oficial o su versión traducida al castellano para profundizar en la sintaxis del lenguaje. Estoy seguro que pronto serás el nuevo gurú de XPath =)2.11 XPath: ejecutando expresiones
package es.davidmarco.soa.xpath;
import java.io.IOException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class Evaluador {
public NodeList filtrarNodos(String expresion, String rutaDocumentoXML) {
NodeList nodosResultado = null;
try {
DocumentBuilderFactory dBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dBuilderFactory.newDocumentBuilder();
Document documentoXML = dBuilder.parse(rutaDocumentoXML);
XPath xpath = XPathFactory.newInstance().newXPath();
nodosResultado = (NodeList) xpath.evaluate(expresion, documentoXML, XPathConstants.NODESET);
} catch (ParserConfigurationException ex) {
ex.printStackTrace();
} catch (SAXException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
} catch (XPathExpressionException ex) {
ex.printStackTrace();
}
return nodosResultado;
}
public static void main(String[] args) {
Evaluador evaluador = new Evaluador();
NodeList nodos = evaluador.filtrarNodos("/parking/plaza", "parking.xml");
for (int i = 0; i < nodos.getLength(); i++) {
System.out.println(nodos.item(i).getTextContent());
}
}
}
NodeList
) que hemos iterado para mostrar por consola el contenido de cada uno de ellos. Realmente simple, ¿verdad?2.12 XPath: validación de expresiones
public NodeList obtenerPlazaPorMatricula(String matricula) {
// ...
String expresion = "/parking/plaza[matricula='" + matricula + "' ", "parking.xml";
return (NodeList) xpath.evaluate(expresion, documentoXML, XPathConstants.NODESET);
}
matricula
contiene un valor malicioso?
' or 1=1 or '
matricula
será la información de todas las plazas, lo que evidentemente es una fuga de información:
AAA-1111
Juan Sánchez
111-88-88
04-12-2014
BBB-2222
Antonio Santos
111-64-64
21-03-2014
CCC-3333
Almudena Gea
111-22-22
01-10-2014
or
, incluyendo el comillado apropiado para que la expresión sea válida y el compilador no lance una excepción de sintaxis:
/parking/plaza[matricula='' or 1=1 or '']
false
, pero la segunda condición devuelve true
y por tanto el resultado global de la expresión es también true
para el valor de todos los elementos <matricula>
. Por tanto, la información de todas las plazas es devuelta.
public static final String ESPACIO_EN_BLANCO = " ";
public static final int INDICE_CERO = 0;
public NodeList obtenerPlazaPorMatricula(String matricula) {
// ...
// Hacemos split del array y seleccionamos el primer valor
String matriculaValidada = matricula.split(ESPACIO_EN_BLANCO)[INDICE_CERO];
String expresion = "/parking/plaza[matricula='" + matriculaValidada + "' ", "parking.xml";
return (NodeList) xpath.evaluate(expresion, documentoXML, XPathConstants.NODESET);
}
Integer.parseInt()
o Float.parseFloat()
hará saltar una excepción en caso de no recibir uno de estos tipos.Resumen