В скорошен проект имах нужда от лесен начин да прекося дървета на обекти на 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']");
По принцип кодът гласи: „Търси всички Department
s от самото начало за 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 може да осигури итератор за всички обекти, извлечени от заявка, подобно на итерацията на набор от резултати. Следният фрагмент показва как можете да итерирате за всички Department
s:
for(Iterator iter = context.iterate("/departmentList"); iter.hasNext();){ Department d = (Department)iter.next(); //... }
За да извлечете всички Employee
s от всички Department
s и да ги повторите:
for(Iterator iter = context.iterate("/departmentList/employees"); iter.hasNext();){ Employee emp = (Employee)iter.next(); //... }
За да изтеглите всички Employee
s по-стари от 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
може да се отнася за „първия служител от втория отдел“. В сравнение с обектите, извлечени директно от дървото, Pointer
s предлагат допълнителни функции като изпълнението на относителни заявки чрез относителни контексти (повече за това по-късно).
Използване на указатели
Наличието на 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 Pointer
s 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 за своите заявки, на разположение е голямо количество справочен материал, който ще ви помогне да изградите ефективни, но сложни заявки за извличане на обекти. Още по-голяма гъвкавост се добавя чрез използване на Pointer
s и относителни контексти.
Тази кратка статия само надрасква повърхността на възможностите на JXPath, за по-задълбочена дискусия с по-напреднали примери за употреба, прочетете пълния ми урок.
Барт ван Риел участва в Java и обектно-ориентирания свят повече от седем години. Работил е както като разработчик, така и като обучител в обектно-ориентираното и Java полетата. В момента той е нает в глобалната ИТ консултантска фирма Capgemini като софтуерен архитект и главен герой.Научете повече за тази тема
- Изтеглете изходния код за тази статия
- Вижте пълния урок за JXPath
- Apache Commons JXPath
- Добър урок за XPath
- Преглед на статиите в JavaWorld е Инструменти за разработка изследователски център
- В крак с новостите в JavaWorld ! Регистрирайте се за нашия безплатен бюлетин за корпоративна Java
Тази история „Заявки за обект на Java с помощта на JXPath“ първоначално е публикувана от JavaWorld.