Лучше всего (и всегда было, даже на Java) использовать JDOM.Я снабдил JDom следующей библиотекой, чтобы она была немного более дружественной к Scala:
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 (чтобы онможет использоваться повторно более одного раза), и вы хотите указать тип, возвращаемый запросом, в точке, где вы указываете текст запроса (не так, как класс XPath JDOM, который выбирает один из четырех методов, вызываемых при выполнении)время).
Пространства имен должны передаваться неявно (чтобы вы могли указать их один раз, а затем забыть о них), а привязка переменных 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
}
Полагаю, мне следует придумать какой-то способ опубликовать это для публики.