Есть ли способ получить «обратный» XPath в Scala? - PullRequest
2 голосов
/ 07 сентября 2011

Если у меня DOM, возможно ли получить обратный XPath элемента?Например, если у меня есть:

<start>
  <nodes>
    <node>
      <name>Whatever</name>
    </node>
    <node>
      <name>Whatever 2</name>
    </node>
  </nodes>
</start>

Если, например, у меня есть ссылка на узел с именем Whatever 2, можно ли вернуть /start/nodes/node/name[. = "Whatever 2"]?

Ответы [ 3 ]

3 голосов
/ 13 сентября 2011

Вот очень простой подход к переходу по дереву с использованием Java DOM API в Scala. REPL:

Сначала мы импортируем соответствующие пакеты и настроим наш конструктор документов и источник:

scala> import org.w3c.dom._
import org.w3c.dom._

scala> import javax.xml.parsers._
import javax.xml.parsers._

scala> val factory = DocumentBuilderFactory.newInstance()
factory: javax.xml.parsers.DocumentBuilderFactory = ...

scala> val builder = factory.newDocumentBuilder()
builder: javax.xml.parsers.DocumentBuilder = ...

scala> val source = new org.xml.sax.InputSource()
source: org.xml.sax.InputSource = org.xml.sax.InputSource@7ecec7c6

Теперь для разбора примера документа:

scala> val content = """<start>
             <nodes>
               <node><name>Whatever</name></node>
               <node><name>Whatever 2</name></node>
             </nodes>
           </start>"""
content: java.lang.String = ...

scala> source.setCharacterStream(new java.io.StringReader(content))

scala> val document = builder.parse(source)
document: org.w3c.dom.Document = [#document: null]

Это очень простая функция, которая рекурсивно перемещает DOM к корню документа:

scala> def path: Node => String = {
     |   case document: Document => ""
     |   case node => path(node.getParentNode) + "/" + node.getNodeName
     | }
path: org.w3c.dom.Node => String

И мы выбираем второй <name> узел для проверки:

scala> val node = document.getElementsByTagName("name").item(1)
node: org.w3c.dom.Node = [name: null]

Мы получаем то, что ожидаем:

scala> path(node)
res1: String = /start/nodes/node/name

Нетрудно настроить функцию path, чтобы избежать явной рекурсии или собрать больше информации по мере продвижения вверх по дереву - например, указывать положение при необходимости, чтобы избежать неоднозначности:

scala> def path(element: Element) = {
     |   def sameName(f: Node => Node)(n: Node) =
     |     Stream.iterate(n)(f).tail.takeWhile(_ != null).filter(
     |       _.getNodeName == n.getNodeName
     |     ).toList
     |   val preceding = sameName(_.getPreviousSibling) _
     |   val following = sameName(_.getNextSibling) _
     |   "/" + Stream.iterate[Node](element)(_.getParentNode).map {
     |     case _: Document => None
     |     case e: Element => Some { (preceding(e), following(e)) match {
     |       case (Nil, Nil) => e.getTagName
     |       case (els, _)   => e.getTagName + "[" + (els.size + 1) + "]"
     |     }}
     |   }.takeWhile(_.isDefined).map(_.get).reverse.mkString("/")
     | }
path: (element: org.w3c.dom.Element)java.lang.String

Обратите внимание, что я немного изменил тип, чтобы было ясно, что это даст нам только правильный путь XPath для элементов. Мы можем проверить:

scala> path(node.asInstanceOf[Element])
res13: java.lang.String = /start/nodes/node[2]/name

Это опять то, что мы ожидаем.

1 голос
/ 15 сентября 2011

Как уже отмечали другие, если все, что у вас есть, это scala.xml.Node, вы не достигнете своей цели, не потратив смехотворное количество времени и места.

Однако, если вы хотите, чтобы ваши абоненты перепрыгивали через несколько обручей, и вам не нравилась идея спуститься на Java, вы могли бы сделать хуже, чем попробовать zipper ,

См. Также реализацию Даниэля Спивака в Anti-XML (вероятно, когда-нибудь заменит встроенную поддержку Scala XML)

0 голосов
/ 12 сентября 2011

Звучит так, будто вы ищете функцию типа path (Node): XPath?К сожалению, это невозможно сделать эффективно с scala.xml, так как узлы не имеют родительской ссылки.Возможные варианты: 1) Поиск в дереве и правильная идентификация, когда вы нашли нужный узел.2) Используйте другую XML-библиотеку (scala или java), которая поддерживает родительские ссылки ... anti-xml и т. Д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...