2010-11-19 40 views
2

给定一个scala XML对象,我可以执行一个xpath字符串查询,如“// entries [@ title ='scala']”?有没有办法使用Scala的XML库来执行XPath字符串查询?

理想的情况下,它会像:

<a><b name='n1'></b></a>.xpath("//b[@name='n1']")

我不能手动转换所有XPath查询到Scala的内部XPath的杂交方法调用我的程序将被动态地接受XPath查询。

此外,内置的java xml库非常冗长,所以我想避免它。

+0

[在Scala中执行复杂的XPath查询]的可能的副本(http://stackoverflow.com/questions/3056337/performing-complicated-xpath-queries-in-scala) – 2010-11-19 19:18:31

回答

4

你最好的选择是(甚至在Java中)使用JDOM。我拉皮条JDOM与下列库是一个更位阶友好:

import org.jdom._ 
import org.jdom.xpath._ 
import scala.collection.JavaConversions 
import java.util._ 
import scala.collection.Traversable 


package pimp.org.jdom{ 
    object XMLNamespace{ 
     def apply(prefix:String,uri:String) = Namespace.getNamespace(prefix,uri) 
     def unapply(x:Namespace) = Some((x.getPrefix, x.getURI)) 
    } 
    object XMLElement{ 
     implicit def wrap(e:Element) = new XMLElement(e) 
     def unapply(x:Element) = Some((x.getName, x.getNamespace)) 
    } 
    class XMLElement(underlying:Element){ 
     def attributes:java.util.List[Attribute] = 
     underlying.getAttributes.asInstanceOf[java.util.List[Attribute]] 
     def children:java.util.List[Element] = 
     underlying.getChildren.asInstanceOf[java.util.List[Element]] 
     def children(name: String): java.util.List[Element] = 
     underlying.getChildren(name).asInstanceOf[java.util.List[Element]] 
     def children(name: String, ns: Namespace): java.util.List[Element] = 
     underlying.getChildren(name, ns).asInstanceOf[java.util.List[Element]] 
    } 
} 

package pimp.org.jdom.xpath{ 
    import pimp.org.jdom._ 

    //instances of these classes are not thread safe when xpath variables are used 

    class SingleNodeQuery[NType](val expression:String)(implicit namespaces:Traversable[Namespace]=null){ 
     private val compiled=XPath.newInstance(expression) 

     if (namespaces!=null){ 
     for (ns <- namespaces) compiled.addNamespace(ns.getPrefix,ns.getURI) 
     } 

     def apply(startFrom:Any,variables:(String,String)*)={ 
     variables.foreach{ x=> compiled.setVariable(x._1,x._2)} 
     compiled.selectSingleNode(startFrom).asInstanceOf[NType] 
     } 
    } 

    class NodesQuery[NType](val expression:String)(implicit namespaces:Traversable[Namespace]=null){ 
     private val compiled=XPath.newInstance(expression) 

     if (namespaces!=null){ 
     for (ns <- namespaces) compiled.addNamespace(ns.getPrefix,ns.getURI) 
     } 

     def apply(startFrom:Any,variables:(String,String)*)={ 
     variables.foreach{ x=> compiled.setVariable(x._1,x._2)} 
     compiled.selectNodes(startFrom).asInstanceOf[java.util.List[NType]] 
     } 
    } 

    class NumberValueQuery(val expression:String)(implicit namespaces:Traversable[Namespace]=null){ 
     private val compiled=XPath.newInstance(expression) 

     if (namespaces!=null){ 
     for (ns <- namespaces) compiled.addNamespace(ns.getPrefix,ns.getURI) 
     } 

     def apply(startFrom:Any,variables:(String,String)*)={ 
     variables.foreach{ x=> compiled.setVariable(x._1,x._2)} 
     compiled.numberValueOf(startFrom).intValue 
     } 
    } 

    class ValueQuery(val expression:String)(implicit namespaces:Traversable[Namespace]=null){ 
     private val compiled=XPath.newInstance(expression) 

     if (namespaces!=null){ 
     for (ns <- namespaces) compiled.addNamespace(ns.getPrefix,ns.getURI) 
     } 

     def apply(startFrom:Any,variables:(String,String)*)={ 
     variables.foreach{ x=> compiled.setVariable(x._1,x._2)} 
     compiled.valueOf(startFrom) 
     } 
    } 

} 

我的想法时,我写这个的是,一般情况下,要编译每个XPath查询提前(这样可以并且您想要指定查询在指定查询文本的位置返回的类型(不像JDOM的XPath类那样,它会在执行时选择要调用的四个方法之一) 。

命名空间应该隐式传递(因此您可以指定它们一次,然后忘记它们),并且XPath变量绑定应该在查询时可用。

你会使用这个库是这样的:(显式类型的注释可以推断 - 我只包括其中一个例子。)

val S = XMLNamespace("s","http://www.nist.gov/speech/atlas") 
val XLink = XMLNamespace("xlink", "http://www.w3.org/1999/xlink") 
implicit val xmlns= List(S, XLink) 

private val anchorQuery=new ValueQuery("s:AnchorRef[@role=$role]/@xlink:href") 

val start:String=anchorQuery(region,"role"->"start") 
val end:String=anchorQuery(region,"role"->"end") 

//or 

private val annotationQuery=new NodesQuery[Element]("/s:Corpus/s:Analysis/s:AnnotationSet/s:Annotation") 

for(annotation:Element <- annotationQuery(doc)) { 
    //do something with it 
} 

我想我应该拿出释放的一些方法这对公众。

+0

让我知道你是否需要帮助打包这个。我可以制作一个简单的maven包,以便它可以轻松地重新使用。 – 2010-11-19 20:42:08

+1

编译您的示例时出现此错误: scala:值foreach不是java.util.List [org.jdom.Element]的成员(注释:元素< - annotationQuery(doc)){ 任何想法? – 2013-02-15 11:06:22

0

kantan.xpath就是这样做的。这里的东西我刚才输入的REPL:

import kantan.xpath._ 
import kantan.xpath.ops._ 

"<a><b name='n1'></b></a>".evalXPath[Node]("//b[@name='n1']") 

,其中Node类型参数描述类型一个希望从XML文档中提取。一个或许更明显的例子是:

new URI("http://stackoverflow.com").evalXPath[List[URI]]("//a/@href") 

这将下载计算器网页,评估其作为XML文档(有一个为HTML禁制一个NekoHTML模块),并提取所有链接的目标。

相关问题