Consultas de objetos Java usando JXPath

En un proyecto reciente, necesitaba una manera fácil de atravesar árboles de objetos Java y extraer valores de los objetos. En lugar de pasar continuamente por enormes configuraciones de iterador-si-más, quería una herramienta que me permitiera simplemente decir, "Quiero el objeto con id = X, y de ese objeto, necesito el valor de la propiedad A". En esencia, necesitaba una herramienta de consulta de objetos.

JXPath es una herramienta de consulta de objetos de este tipo. Es un componente de Apache Commons que le permite consultar árboles de objetos complejos utilizando el conocido lenguaje de expresión XPath. Empleé JXPath ampliamente en mi proyecto y aceleró considerablemente las cosas, haciendo que los algoritmos de extracción de valor fueran muy sencillos.

Sin embargo, JXPath no está ampliamente documentado. Como de todos modos estaba explorando el componente en profundidad, decidí anotar mis hallazgos en un extenso tutorial de JXPath, que puede encontrar en mi sitio web. Este artículo es una versión abreviada de ese tutorial para que pueda comenzar con JXPath rápidamente.

Nota: Puede descargar el código de muestra adjunto de Resources.

Modelo de ejemplo

Con fines ilustrativos, usaremos un modelo simple: una empresa con varios departamentos , cada uno con varios empleados . Aquí está el modelo de clase:

Naturalmente, necesitamos algunos datos de muestra para el modelo:

Empresa

Departamento

Empleado (nombre, cargo, edad)

Acme Inc.

Ventas

Johnny, representante de ventas, 45

Sarah, representante de ventas, 33

Magda, asistente de oficina, 27

Contabilidad

Steve, controlador principal, 51

Peter, asistente del controlador, 31

Susan, asistente de oficina, 27

Con eso en su lugar, ¡comencemos a usar JXPath!

Ejecución de consultas JXPath simples

La consulta más simple posible extrae un solo objeto del árbol de objetos. Por ejemplo, para recuperar Company, use el siguiente código:

JXPathContext context = JXPathContext.newContext(company); Company c = (Company)context.getValue(".");

La primera línea muestra la creación de a context, el punto de partida para todas las expresiones XPath de JXPath en el árbol de objetos (comparable al rootnodeelemento en un documento XML). La segunda línea de código ejecuta la consulta real. Dado que nuestro contextcomienzo a nivel de empresa, para recuperar el Companyobjeto, simplemente usamos el selector de elemento actual '.'.

Usar predicados y variables

An Employeees un objeto hijo de a Department. Para recuperar el Employeenombre "Johnny", utilice el siguiente código ( Companysigue siendo contextel punto de partida):

Employee emp = (Employee)context.getValue("/departmentList/employees[name='Johnny']");

Básicamente, el código dice: "Buscar en todos los Departmentmensajes de correo electrónico desde el principio el Employeeobjeto cuyo namevalor tiene el atributo 'Johnny'".

El fragmento de código anterior ilustra cómo usar un predicado para buscar objetos usando valores particulares. El uso de predicados es comparable al uso de la cláusula WHERE en SQL. Incluso podemos combinar varios predicados en una consulta:

Employee emp = (Employee)context.getValue("/departmentList/employees[name='Susan' and age=27]");

A menos que esté utilizando una consulta ad-hoc y única, la implementación de consultas codificadas por lo general no es factible. Es mejor definir una consulta reutilizable que luego pueda ejecutar con diferentes parámetros. Para adaptarse a las consultas parametrizadas, JXPath admite variables en las consultas. Usando variables, el código anterior ahora se ve así:

context.getVariables().declareVariable("name", "Susan"); context.getVariables().declareVariable("age", new Integer(27)); Employee emp = (Employee)context.getValue("/departmentList/employees[name=$name and age=$age]");

Iterando colecciones

JXPath puede proporcionar un iterador sobre todos los objetos recuperados por una consulta, al igual que iterar un conjunto de resultados. El siguiente fragmento muestra cómo puede iterar sobre todos Departmentlos mensajes de correo electrónico :

for(Iterator iter = context.iterate("/departmentList"); iter.hasNext();){ Department d = (Department)iter.next(); //... }

Para recuperar todos Employeelos Departmentcorreos electrónicos de todos los correos electrónicos e iterar sobre ellos:

for(Iterator iter = context.iterate("/departmentList/employees"); iter.hasNext();){ Employee emp = (Employee)iter.next(); //... }

Para recuperar todos Employeelos correos electrónicos con más de 30 años del departamento de ventas:

for(Iterator iter = context.iterate ("/departmentList[name='Sales']/employees[age>30]"); iter.hasNext();){ Employee emp = (Employee)iter.next(); //... }

Y el ejemplo anterior con variables:

context.getVariables().declareVariable("deptName", "Sales"); context.getVariables().declareVariable("minAge", new Integer(30)); for(Iterator iter = context.iterate("/departmentList [name=$deptName]/employees[age>$minAge]"); iter.hasNext();){ Employee emp = (Employee)iter.next(); //... }

Estos dos últimos fragmentos de código también demuestran el uso de varios predicados dentro de una consulta XPath.

Punteros

A Pointeres un objeto de utilidad JXPath que representa una referencia a la ubicación de un objeto en el árbol de objetos. Por ejemplo, a Pointerpodría referirse al "primer empleado del segundo departamento". En comparación con los objetos recuperados directamente del árbol, los Pointers ofrecen funciones adicionales como la ejecución de consultas relativas a través de contextos relativos (más sobre esto más adelante).

Usando punteros

Tener una Pointerreferencia a un objeto en el árbol de objetos es casi idéntico a recuperar objetos directamente:

JXPathContext context = JXPathContext.newContext(company); Pointer empPtr = context.getPointer("/departmentList[name='Sales']/employees[age>40]"); System.out.println(empPtr); //output: /departmentList[1]/employees[1] System.out.println(((Employee)empPtr.getValue()).getName()); //output: Johnny

Note that the Pointer's output demonstrates that a Pointer describes an object's location, rather than the object itself. Also note that the actual object the Pointer refers to can be retrieved through the Pointer's getValue() method.

Pointers can also be iterated over, as the following snippet demonstrates:

for(Iterator iter = context.iteratePointers("/departmentList[name='Sales'] /employees[age>30]"); iter.hasNext();){ Pointer empPtr = (Pointer)iter.next(); //... }

Relative context and relative queries

Since a Pointer describes a location, it can be used as a reference point for navigating through the entire object tree. To do that, use the Pointer as the root object (Remember using the Company object for that earlier?) in a so called relative context. From this relative context, you can query the entire object tree by executing relative queries. This advanced use of Pointers offers great flexibility as the examples below illustrate.

To begin, here's how you create a relative context:

for(Iterator iter = context.iteratePointers("/departmentList[name='Sales'] /employees[age>30]"); iter.hasNext();){ Pointer empPtr = (Pointer)iter.next(); JXPathContext relativeContext = context.getRelativeContext(empPtr); }

In this code snippet, a new relative context is created for consecutive employee pointers.

Using the relative context, XPath queries can be executed on the entire object tree of siblings, children, and parent/grandparent objects, as the following snippet demonstrates:

//Current employee Employee emp = (Employee)relativeContext.getValue("."); //Employee name String name = (String)relativeContext.getValue("./name"); //Name of the Department this Employee belongs to (a parent object) String deptName = (String)relativeContext.getValue("../name"); //Name of the Company this Employee belongs to (a 'grandparent' object) String compName = (String)relativeContext.getValue("../../name"); //All coworkers of this Employee (sibling objects) for(Iterator empIter = relativeContext.iterate("../employees"); empIter.hasNext();){ Employee colleague = (Employee)empIter.next(); //... }

Summary

JXPath es una herramienta extremadamente útil para recorrer, navegar y consultar árboles de objetos complejos. Debido a que utiliza el lenguaje de expresión XPath para sus consultas, se encuentra disponible una gran cantidad de material de referencia para ayudarlo a crear consultas de recuperación de objetos eficientes pero complejas. Se agrega aún más flexibilidad al usar Pointersy contextos relativos.

Este breve artículo solo rasca la superficie de las posibilidades de JXPath, para una discusión más profunda con ejemplos de uso más avanzados, lea mi tutorial completo.

Bart van Riel ha estado involucrado en Java y en el mundo orientado a objetos durante más de siete años. Ha trabajado como desarrollador y formador en los campos orientado a objetos y Java. Actualmente es empleado de la consultora global de TI Capgemini como arquitecto de software y protagonista de código abierto.

Más información sobre este tema

  • Descarga el código fuente de este artículo
  • Consulte el tutorial completo de JXPath
  • Apache Commons JXPath
  • Un buen tutorial de XPath
  • Navegar a través de los artículos en JavaWorld 's Herramientas de desarrollo del Centro de Investigación
  • ¡Manténgase al día con las novedades de JavaWorld ! Suscríbase a nuestro boletín informativo gratuito Enterprise Java

Esta historia, "Consultas de objetos Java usando JXPath" fue publicada originalmente por JavaWorld.