使用JXPath查询Java对象

使用JXPath查询Java对象
—使用XPath表达式语言查询复杂的Java对象树


译者: cleverpig
原文作者:Bart van Riel
原文出处: http://www.javaworld.com/javaworld/jw-03-2007/jw-03-jxpath.html

         在近期的一个项目中,我需要一种能够遍历Java对象树并从中提取对象属性值的简单方法。我希望找到一种提供 “我所想要的id为X并且具有属性A的对象”的简单工具,来顶替传统的不优雅方式——通过巨大的if-else迭代器设置对对象树进行不断地遍历。         

        而 JXPath正是我想找的那种对象查询工具。它是一个Apache公用组件:使用众所周知的 XPath表达式语言,能够对复杂的对象树进行查询。因此我将JXPath广泛地用在项目中,并且由于通过它的值提取算法(value-extraction algorithms)使应用获得了很大程度上的加速。        

        但是,JXPath并没有被广泛地文档化。因为我正在深度探索此组件,所以我决定将我的发现写到一个内容详实的JXPath入门中,这个入门可以在 我的站点上找到。本文是这个入门的精简版本,它可以帮助你快速上手。        
                        
        注意:你可以从相关资源中下载本文的示例代码。

示例模型

        为了利用展示的目的,我们将使用一个简单的模型:一个具有多个部门(department)的公司(company),其中每个部门有多个雇员(employee)。下面是类模型:

Company类结构图

        很自然,我们需要一些用于该模型的样本数据:


         一切就绪了,现在就让我们开始使用JXPath吧!

执行简单的JXPath查询

        最简单的查询就是从对象树中提取单一对象。例如,为了取出Company对象,我们将使用到如下代码:
JXPathContext context = JXPathContext.newContext(company);
Company c = (Company)context.getValue(".");

         第一行展示了如何建立一个上下文(context),它是对象树中所有JXPath的Xpath表达式的起点(与XML文档中的根节点类似)。第二行代码 执行了实际的查询。由于我们的上下文从company层开始,所以为了获取Company对象,我们简单地使用当前成员选择器“.”。

使用判定(predicate)和变量        

        Employee是Department的子对象。为了获取名为“Johnny”的Employee对象使用如下代码(注意此时Company仍然是上下文的起点):
Employee emp = (Employee)context.getValue("/departmentList/employees[name='Johnny']");

        代码大意为:“从起点开始查询所有Department中name属性值为Johnny俄Employee对象”。

        上面的代码说明了如何通过使用特定值来利用判定进行对象查询。判定的用法类似在SQL中使用WHERE子句。我们甚至能够将多个判定合并在一个查询中:
Employee emp = 
   (Employee)context.getValue("/departmentList/employees[name='Susan' and age=27]");


        除非使用特定的、一次性的查询,使用硬编码的查询通称是不合理的。最好定义一个能够使用不同参数执行的、可重用的查询。为了提供这种参数化的查询,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能够提供作用于查询获取的所有对象的迭代器,就像SQL中迭代结果集一样。下面的代码片段展示了如何对所有的Department进行迭代:
for(Iterator iter = context.iterate("/departmentList"); iter.hasNext();){
   Department d = (Department)iter.next();
   //...
}

        获取所有Department的所有Employee并进行迭代:
for(Iterator iter = context.iterate("/departmentList/employees"); iter.hasNext();){
   Employee emp = (Employee)iter.next();
   //...
}

        为了取得在销售部门的所有年龄大于30岁的Employee:
 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查询中的使用判定的方式。

指针        

        指针是一个JXPath工具对象,它代表了在对象树中对对象位置的引用。例如,指针可以指向“第二个department中的第一个employee”。与直接从对象树中获取对象相比,指针提供了通过相关上下文执行相关查询的功能。

使用指针

        使用指向对象树中某个对象的指针几乎和直接获取对象的编码都很简单:
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

        请注意这里指针的输出展示了指针表示了对象的位置,而不是对象本身。也要注意指针指向的实际对象可以通过指针的getValue()方法获得。

        指针也具有迭代器:
for(Iterator iter = context.iteratePointers("/departmentList[name='Sales']
     /employees[age>30]"); iter.hasNext();){
   Pointer empPtr = (Pointer)iter.next();
   //...
}


相对上下文和相对查询

         由于指针表示了对象的位置,所以它能够被用作在整个对象树中进行“航行”的参考点。为了实现这一点,将指针作为被称为“相对上下文”的根对象(还记得起在 前面示例中使用Company作为根节点吗?)。从这个相对上下文出发,你可以通过通过执行相对查询来对整个对象树进行查询。指针提供的这一优势为查询提 供了很大灵活性,下面将举例说明。

        首先,这是建立相对上下文的方式:
for(Iterator iter = context.iteratePointers("/departmentList[name='Sales']
     /employees[age>30]"); iter.hasNext();){
   Pointer empPtr = (Pointer)iter.next();
   JXPathContext relativeContext = context.getRelativeContext(empPtr);
}

        在这段代码中,为连续的employee指针建立了一个新的相对上下文。
        通过使用相对上下文,XPath能够在整个对象树上兄弟节点、子节点、父节点、祖父节点对象上执行查询:
//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();
   //...
}


总结

        JXPath是一种用于遍历、查询复杂对象树,并在其中进行“航行”的极其有用的工具。因为它将XPath表达式语言用于查询,因此大量关于XPath的参考资料有助于你建立高效、复杂的对象查询。甚至可以通过使用指针和相对上下文来增加更多的灵活性。

        本文仅仅蜻蜓点水式地涉及到了JXPath的几个可能的方面,如果要获得更多的使用示例和深度的讨论,请阅读 我写的完整的入门。

作者经历

        Bart van Riel已经涉足Java和面向对象时间达七年多了。他即是一位开发者又是一位面向对象和Java领域的培训师。目前他作为软件架构师和开源倡导者任职于全球IT咨询公司 Capgemini。

相关资源

         下载本文源代码
         Bart van Riel编写的完整的JXPath入门
         Apache Commons JXPath
         W3C提供的XPath入门

你可能感兴趣的:(默认类别,Java,领域模型,咨询,SQL,Apache)