Interrogazioni di oggetti Java utilizzando JXPath

In un progetto recente, avevo bisogno di un modo semplice per attraversare alberi di oggetti Java ed estrarre valori dagli oggetti. Invece di passare continuamente attraverso enormi configurazioni di iteratore-se-altro, volevo uno strumento che mi permettesse di dire semplicemente: "Voglio l'oggetto con id = X, e da quell'oggetto, ho bisogno del valore della proprietà A." In sostanza, avevo bisogno di uno strumento di interrogazione degli oggetti.

JXPath è un tale strumento di query di oggetti. Si tratta di un componente Apache Commons che consente di eseguire query su alberi di oggetti complessi utilizzando il noto linguaggio di espressione XPath. Ho utilizzato JXPath ampiamente nel mio progetto e ha velocizzato notevolmente le cose, rendendo gli algoritmi di estrazione del valore un gioco da ragazzi.

Tuttavia, JXPath non è ampiamente documentato. Poiché comunque stavo esplorando il componente in modo approfondito, ho deciso di scrivere i miei risultati in un ampio tutorial su JXPath, che puoi trovare sul mio sito Web. Questo articolo è una versione abbreviata di quel tutorial per iniziare rapidamente con JXPath.

Nota: è possibile scaricare il codice di esempio allegato da Resources.

Modello di esempio

A scopo illustrativo, utilizzeremo un modello semplice: un'azienda con vari reparti , ciascuno con diversi dipendenti . Ecco il modello di classe:

Naturalmente, abbiamo bisogno di alcuni dati di esempio per il modello:

Azienda

Dipartimento

Dipendente (nome, titolo professionale, età)

Acme Inc.

I saldi

Johnny, rappresentante di vendita, 45

Sarah, rappresentante di vendita, 33

Magda, assistente d'ufficio, 27

Contabilità

Steve, capo controllore, 51

Peter, assistente controllore, 31

Susan, assistente d'ufficio, 27

Con questo in atto, iniziamo a utilizzare JXPath!

Esecuzione di semplici query JXPath

La query più semplice possibile estrae un singolo oggetto dall'albero degli oggetti. Ad esempio, per recuperare Company, utilizzare il codice seguente:

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

La prima riga mostra la creazione di un contextpunto di partenza per tutte le espressioni XPath di JXPath nell'albero degli oggetti (paragonabile rootnodeall'elemento in un documento XML). La seconda riga di codice esegue la query effettiva. Poiché il nostro contextinizia a livello aziendale, per recuperare l' Companyoggetto, utilizziamo semplicemente il selettore dell'elemento corrente '.'.

Utilizzo di predicati e variabili

An Employeeè un oggetto figlio di a Department. Per recuperare il Employeenome "Johnny" usa il seguente codice ( Companyè ancora contextil punto di partenza):

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

Fondamentalmente, il codice dice: "Cerca in tutti Departmenti messaggi dall'inizio per l' Employeeoggetto di cui l' nameattributo ha il valore 'Johnny'."

Lo snippet di codice precedente illustra come utilizzare un predicato per cercare oggetti utilizzando valori particolari. L'utilizzo dei predicati è paragonabile all'utilizzo della clausola WHERE in SQL. Possiamo persino combinare più predicati in una query:

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

A meno che tu non stia utilizzando una query ad hoc e una tantum, l'implementazione di query hard-coded di solito non è fattibile. È meglio definire una query riutilizzabile che è possibile eseguire successivamente con parametri diversi. Per soddisfare le query con parametri, JXPath supporta le variabili nelle query. Usando le variabili, il codice sopra ora assomiglia a questo:

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

Iterazione sulle raccolte

JXPath può fornire un iteratore su tutti gli oggetti recuperati da una query, proprio come l'iterazione di un set di risultati. Il seguente frammento mostra come è possibile iterare su tutti Departmenti messaggi:

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

Per recuperare tutti Employeei messaggi da tutti Departmenti messaggi e ripeterli:

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

Per recuperare tutti Employeei messaggi di posta elettronica più vecchi di 30 anni dal reparto vendite:

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

E l'esempio sopra con le variabili:

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(); //... }

Questi due ultimi frammenti di codice dimostrano anche l'uso di diversi predicati all'interno di una query XPath.

Puntatori

A Pointerè un oggetto di utilità JXPath che rappresenta un riferimento alla posizione di un oggetto nell'albero degli oggetti. Ad esempio, a Pointerpotrebbe fare riferimento a "il primo dipendente del secondo dipartimento". Rispetto agli oggetti recuperati direttamente dall'albero, Pointers offre funzioni aggiuntive come l'esecuzione di query relative attraverso contesti relativi (ne parleremo più avanti).

Utilizzo dei puntatori

Avere un Pointerriferimento a un oggetto nella struttura ad albero degli oggetti è quasi identico al recupero diretto degli oggetti:

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 è uno strumento estremamente utile per attraversare, navigare e interrogare alberi di oggetti complessi. Poiché utilizza il linguaggio delle espressioni XPath per le sue query, è disponibile un ampio corpo di materiale di riferimento per aiutarti a creare query di recupero di oggetti efficienti ma complesse. Ancora più flessibilità viene aggiunta utilizzando se Pointeri relativi contesti.

Questo breve articolo graffia solo la superficie delle possibilità di JXPath, per una discussione più approfondita con esempi di utilizzo più avanzati, leggi il mio tutorial completo.

Bart van Riel è stato coinvolto nel mondo Java e orientato agli oggetti da più di sette anni. Ha lavorato sia come sviluppatore che come formatore nei campi orientati agli oggetti e Java. Attualmente è impiegato dalla società di consulenza IT globale Capgemini come architetto di software e protagonista dell'open source.

Ulteriori informazioni su questo argomento

  • Scarica il codice sorgente per questo articolo
  • Guarda il tutorial completo di JXPath
  • Apache Commons JXPath
  • Un buon tutorial su XPath
  • Sfoglia attraverso gli articoli in JavaWorld 's strumenti di sviluppo Research Center
  • Tieniti aggiornato sulle novità di JavaWorld ! Iscriviti alla nostra newsletter gratuita Enterprise Java

Questa storia, "query di oggetti Java utilizzando JXPath" è stata originariamente pubblicata da JavaWorld.