Java-Objektabfragen mit JXPath

In einem kürzlich durchgeführten Projekt brauchte ich eine einfache Möglichkeit, Java-Objektbäume zu durchlaufen und Werte aus den Objekten zu extrahieren. Anstatt ständig große Iterator-wenn-sonst-Setups zu durchlaufen, wollte ich ein Tool, mit dem ich einfach sagen kann: "Ich möchte das Objekt mit id = X, und von diesem Objekt benötige ich den Wert der Eigenschaft A." Im Wesentlichen brauchte ich ein Tool zur Objektabfrage.

JXPath ist ein solches Objektabfragetool. Es handelt sich um eine Apache Commons-Komponente, mit der Sie komplexe Objektbäume mithilfe der bekannten XPath-Ausdruckssprache abfragen können. Ich habe JXPath in meinem Projekt weit verbreitet eingesetzt, und es hat die Dinge erheblich beschleunigt, sodass Algorithmen zur Wertextraktion zum Kinderspiel wurden.

JXPath ist jedoch nicht umfassend dokumentiert. Da ich die Komponente ohnehin eingehend untersucht habe, habe ich beschlossen, meine Ergebnisse in einem umfangreichen JXPath-Tutorial niederzuschreiben, das Sie auf meiner Website finden. Dieser Artikel ist eine Kurzversion dieses Tutorials, damit Sie schnell mit JXPath beginnen können.

Hinweis: Sie können den zugehörigen Beispielcode von Resources herunterladen.

Beispielmodell

Zur Veranschaulichung verwenden wir ein einfaches Modell: ein Unternehmen mit verschiedenen Abteilungen mit jeweils unterschiedlichen Mitarbeitern . Hier ist das Klassenmodell:

Natürlich benötigen wir einige Beispieldaten für das Modell:

Unternehmen

Abteilung

Mitarbeiter (Name, Berufsbezeichnung, Alter)

Acme Inc.

Der Umsatz

Johnny, Vertriebsmitarbeiter, 45

Sarah, Vertriebsmitarbeiterin, 33

Magda, Büroassistentin, 27

Buchhaltung

Steve, Head Controller, 51

Peter, stellvertretender Controller, 31

Susan, Büroassistentin, 27

Lassen Sie uns JXPath verwenden!

Ausführen einfacher JXPath-Abfragen

Die einfachste mögliche Abfrage extrahiert ein einzelnes Objekt aus dem Objektbaum. CompanyVerwenden Sie zum Abrufen beispielsweise den folgenden Code:

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

Die erste Zeile zeigt die Erstellung von a context, dem Ausgangspunkt für alle XPath-Ausdrücke von JXPath im Objektbaum (vergleichbar mit dem rootnodeElement in einem XML-Dokument). Die zweite Codezeile führt die eigentliche Abfrage aus. Da wir contextauf Unternehmensebene beginnen Company, verwenden wir zum Abrufen des Objekts einfach den Selektor für aktuelle Elemente '.'.

Prädikate und Variablen verwenden

An Employeeist ein untergeordnetes Objekt von a Department. Um den EmployeeNamen "Johnny" abzurufen, verwenden Sie den folgenden Code ( Companyist immer noch contextder Ausgangspunkt):

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

Grundsätzlich lautet der Code: "Durchsuchen Sie alle Departments von Anfang an nach dem EmployeeObjekt, dessen nameWert das Attribut hat 'Johnny'."

Das obige Codeausschnitt zeigt, wie ein Prädikat zum Suchen von Objekten unter Verwendung bestimmter Werte verwendet wird. Die Verwendung von Prädikaten ist vergleichbar mit der Verwendung der WHERE-Klausel in SQL. Wir können sogar mehrere Prädikate in einer Abfrage kombinieren:

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

Sofern Sie keine einmalige Ad-hoc-Abfrage verwenden, ist die Implementierung fest codierter Abfragen normalerweise nicht möglich. Es ist besser, eine wiederverwendbare Abfrage zu definieren, die Sie dann mit verschiedenen Parametern ausführen können. Um parametrisierte Abfragen zu ermöglichen, unterstützt JXPath Variablen in Abfragen. Unter Verwendung von Variablen sieht der obige Code nun folgendermaßen aus:

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

Durch Sammlungen iterieren

JXPath kann einen Iterator für alle Objekte bereitstellen, die von einer Abfrage abgerufen werden, ähnlich wie das Iterieren einer Ergebnismenge. Das folgende Snippet zeigt, wie Sie über alle Departments iterieren können :

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

So rufen Sie alle Employees von allen Departments ab und durchlaufen sie:

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

So rufen Sie alle Personen ab Employee30 Jahren aus der Verkaufsabteilung ab:

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

Und das obige Beispiel mit Variablen:

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

Diese beiden letzten Codefragmente demonstrieren auch die Verwendung mehrerer Prädikate innerhalb einer XPath-Abfrage.

Zeiger

A Pointerist ein JXPath-Dienstprogrammobjekt, das einen Verweis auf die Position eines Objekts im Objektbaum darstellt. Beispielsweise Pointerkönnte sich a auf "den ersten Mitarbeiter der zweiten Abteilung" beziehen. Im Vergleich zu Objekten, die direkt aus dem Baum abgerufen werden, Pointerbieten s zusätzliche Funktionen wie die Ausführung relativer Abfragen über relative Kontexte (dazu später mehr).

Zeiger verwenden

Ein PointerVerweis auf ein Objekt im Objektbaum ist fast identisch mit dem direkten Abrufen von Objekten:

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 ist ein äußerst nützliches Tool zum Durchlaufen, Navigieren und Abfragen komplexer Objektbäume. Da für seine Abfragen die XPath-Ausdruckssprache verwendet wird, steht eine Vielzahl von Referenzmaterialien zur Verfügung, mit denen Sie effiziente und dennoch komplexe Abfragen zum Abrufen von Objekten erstellen können. Noch mehr Flexibilität wird durch die Verwendung von Pointers und relativen Kontexten hinzugefügt .

Dieser kurze Artikel kratzt nur an der Oberfläche der Möglichkeiten von JXPath. Lesen Sie mein vollständiges Tutorial, um eine ausführlichere Diskussion mit fortgeschritteneren Anwendungsbeispielen zu erhalten.

Bart van Riel ist seit mehr als sieben Jahren in der Java- und objektorientierten Welt tätig. Er hat sowohl als Entwickler als auch als Trainer im objektorientierten und Java-Bereich gearbeitet. Derzeit ist er beim globalen IT-Beratungsunternehmen Capgemini als Softwarearchitekt und Open Source-Protagonist beschäftigt.

Erfahren Sie mehr über dieses Thema

  • Laden Sie den Quellcode für diesen Artikel herunter
  • Schauen Sie sich das vollständige JXPath-Tutorial an
  • Apache Commons JXPath
  • Ein gutes XPath-Tutorial
  • Blättern Sie durch die Artikel in Javaworld ‚s - Entwicklungstools Research Center
  • Bleiben Sie auf dem Laufenden bei JavaWorld ! Melden Sie sich für unseren kostenlosen Enterprise Java- Newsletter an

Diese Geschichte "Java-Objektabfragen mit JXPath" wurde ursprünglich von JavaWorld veröffentlicht.