Заявки за обекти на Java с помощта на JXPath

В скорошен проект имах нужда от лесен начин да прекося дървета на обекти на Java и да извлека стойности от обектите. Вместо непрекъснато да преминавам през огромни настройки на итератор-ако-друго, исках инструмент, който да ми позволи просто да кажа: „Искам обекта с id = X и от този обект имам нужда от стойността на свойство А.“ По същество имах нужда от инструмент за заявки за обекти.

JXPath е такъв инструмент за заявки за обект. Това е компонент на Apache Commons, който ви позволява да заявявате сложни дървета на обекти, използвайки добре познатия език за израз на XPath. Използвах JXPath широко в моя проект и той ускори нещата значително, правейки алгоритмите за извличане на стойност бриз.

JXPath обаче не е широко документиран. Тъй като така или иначе проучвах компонента задълбочено, реших да запиша констатациите си в обширен урок за JXPath, който можете да намерите на моя уеб сайт. Тази статия е съкратена версия на този урок, за да започнете бързо с JXPath.

Забележка: Можете да изтеглите придружаващия примерен код от ресурси.

Примерен модел

За целите на илюстрацията ще използваме прост модел: компания с различни отдели , всеки с различни служители . Ето модела на класа:

Естествено, ние се нуждаем от някои примерни данни за модела:

Компания

Отдел

Служител (име, длъжност, възраст)

Acme Inc.

Продажби

Джони, търговски представител, 45

Сара, търговски представител, 33

Магда, офис асистент, 27

Счетоводство

Стив, главен контролер, 51

Питър, помощник-контролер, 31

Сюзън, офис асистент, 27

С това на място, нека започнем да използваме JXPath!

Изпълнение на прости JXPath заявки

Възможно най-простата заявка извлича отделен обект от дървото на обектите. Например, за да извлечете Company, използвайте следния код:

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

Първият ред показва създаването на a context, начална точка за всички изрази XPath на JXPath в дървото на обекта (сравним с rootnodeелемента в XML документ). Вторият ред код изпълнява действителната заявка. Тъй като нашите contextзапочват на ниво компания, за да извлечем Companyобекта, ние просто използваме селектора на текущия елемент '.'.

Използване на предикати и променливи

An Employeeе дъщерен обект на a Department. За да извлечете Employeeимената "Джони", използвайте следния код ( Companyвсе още contextе начална точка):

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

По принцип кодът гласи: „Търси всички Departments от самото начало за Employeeобекта, чийто nameатрибут има стойността 'Johnny'.“

Горният кодов фрагмент илюстрира как да използваме предикат за търсене на обекти, като използваме определени стойности. Използването на предикати е сравнимо с използването на клаузата WHERE в SQL. Можем дори да комбинираме множество предикати в една заявка:

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

Освен ако не използвате ad hoc, еднократна заявка, внедряването на твърдо кодирани заявки обикновено не е осъществимо. По-добре е да дефинирате заявка за многократна употреба, която след това можете да изпълните с различни параметри. За да приспособи параметризирано заявка, JXPath поддържа променливи в заявки. Използвайки променливи, горният код сега изглежда така:

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

Итерация върху колекции

JXPath може да осигури итератор за всички обекти, извлечени от заявка, подобно на итерацията на набор от резултати. Следният фрагмент показва как можете да итерирате за всички Departments:

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

За да извлечете всички Employees от всички Departments и да ги повторите:

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

За да изтеглите всички Employees по-стари от 30 години от отдела за продажби:

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

И горният пример с променливи:

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

Тези два последни кодови фрагмента също демонстрират използването на няколко предиката в рамките на една XPath заявка.

Указатели

A Pointerе JXPath помощен обект, който представлява препратка към местоположението на обект в дървото на обекта. Например, a Pointerможе да се отнася за „първия служител от втория отдел“. В сравнение с обектите, извлечени директно от дървото, Pointers предлагат допълнителни функции като изпълнението на относителни заявки чрез относителни контексти (повече за това по-късно).

Използване на указатели

Наличието на Pointerпрепратка към обект в дървото на обекти е почти идентично с директно извличане на обекти:

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 е изключително полезен инструмент за обхождане, навигация и заявки за сложни дървета на обекти. Тъй като той използва езика за изразяване XPath за своите заявки, на разположение е голямо количество справочен материал, който ще ви помогне да изградите ефективни, но сложни заявки за извличане на обекти. Още по-голяма гъвкавост се добавя чрез използване на Pointers и относителни контексти.

Тази кратка статия само надрасква повърхността на възможностите на JXPath, за по-задълбочена дискусия с по-напреднали примери за употреба, прочетете пълния ми урок.

Барт ван Риел участва в Java и обектно-ориентирания свят повече от седем години. Работил е както като разработчик, така и като обучител в обектно-ориентираното и Java полетата. В момента той е нает в глобалната ИТ консултантска фирма Capgemini като софтуерен архитект и главен герой.

Научете повече за тази тема

  • Изтеглете изходния код за тази статия
  • Вижте пълния урок за JXPath
  • Apache Commons JXPath
  • Добър урок за XPath
  • Преглед на статиите в JavaWorld е Инструменти за разработка изследователски център
  • В крак с новостите в JavaWorld ! Регистрирайте се за нашия безплатен бюлетин за корпоративна Java

Тази история „Заявки за обект на Java с помощта на JXPath“ първоначално е публикувана от JavaWorld.