有没有办法使用 Scala 的 XML 库执行 XPath 字符串查询? [英] Is there a way to perform a XPath string query using Scala's XML library?

查看:36
本文介绍了有没有办法使用 Scala 的 XML 库执行 XPath 字符串查询?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给定一个 scala XML 对象,我可以执行像//entries[@title='scala']"这样的 xpath 字符串查询吗?

Given an scala XML object, can I perform a xpath string query like "//entries[@title='scala']" ?

理想情况下,它应该是这样的:

Ideally, it would be like:

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

我无法手动将所有 xpath 查询转换为 Scala 的内部 xpath-ish 方法调用,因为我的程序将动态接受 xpath 查询.

I can't manually convert all the xpath queries to scala's internal xpath-ish method calls as my program will is dynamically accepting xpath queries.

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

Additionally, the built-in java xml library is very verbose so I would like to avoid it.

推荐答案

您最好的选择是(并且一直是,即使在 Java 中)使用 JDOM.我用以下库对 JDom 进行了拉皮条,以使其对 Scala 更加友好:

Your best bet is (and always was, even in Java) to use JDOM. I've pimped JDom with the following library to be a bit more scala friendly:

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 类那样在执行时选择要调用的四种方法之一).

My idea when I wrote this was that in general, you want to compile each XPath query in advance (so that it can be reused more than once), and that you want to specify the type returned by the query at the point where you specify the text of the query (not like JDOM's XPath class does which is to pick one of four methods to call at execution time).

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

Namespaces should be passed around implicitly (so you can specify them once and then forget about them), and XPath variable binding should be available at query time.

您会像这样使用库:(可以推断显式类型注释——我将它们包含在内仅用于说明.)

You'd use the library like this: (Explicit type annotations can be inferred -- I've included them for illustration only.)

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
}

我想我应该想办法向公众发布这个.

I guess I should come up with some way of releasing this to the public.

这篇关于有没有办法使用 Scala 的 XML 库执行 XPath 字符串查询?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆