Преобразование org.w3c.dom.NodeList в Clojure ISeq - PullRequest
6 голосов
/ 05 мая 2011

Я пытаюсь получить ручку на новых defprotocol, reify и т. Д.

Я получил org.w3c.dom.NodeList, возвращенный из вызова XPath, и я хотел бы "преобразовать" его в ISeq.

В Scala я реализовал метод неявного преобразования:

implicit def nodeList2Traversable(nodeList: NodeList): Traversable[Node] = {
  new Traversable[Node] {
    def foreach[A](process: (Node) => A) {
      for (index <- 0 until nodeList.getLength) {
        process(nodeList.item(index))
      }
    }
  }
}

NodeList включает методы int getLength() и Node item(int index).

Как мне сделать эквивалент в Clojure? Я ожидаю, что мне нужно будет использовать defprotocol. Какие функции мне нужно определить, чтобы создать seq?

Если я сделаю простое, наивное преобразование в список, используя loop и recur, я получу в итоге ленивую структуру.

Ответы [ 2 ]

7 голосов
/ 05 мая 2011

Большинство функций обработки последовательности Clojure возвращают ленивые последовательности, в том числе функции map и range:

(defn node-list-seq [^org.w3c.dom.NodeList node-list]
  (map (fn [index] (.item node-list index))
       (range (.getLength node-list))))

Обратите внимание, что указание типа для NodeList выше не является необходимым, но повышает производительность.

Теперь вы можете использовать эту функцию следующим образом:

(map #(.getLocalName %) (node-list-seq your-node-list))
6 голосов
/ 05 мая 2011

Используйте для понимания , это приводит к ленивым последовательностям.

Вот код для вас.Я нашел время, чтобы запустить его в командной строке;вам нужно только заменить имя проанализированного XML-файла.

Предупреждение 1: избегайте определения переменных.Вместо этого используйте локальные переменные.

Предупреждение 2: это Java API для XML, поэтому объекты могут изменяться;поскольку у вас есть ленивая последовательность, если какие-либо изменения произойдут с изменяемым DOM-деревом во время итерации, у вас могут возникнуть неприятные изменения расы.

Предупреждение 3: , даже если это ленивыйструктура, все дерево DOM уже находится в памяти в любом случае (хотя я не совсем уверен в этом последнем комментарии, хотя. Я думаю, что API пытается отложить чтение дерева в памяти до необходимости, но без гарантий) .Поэтому, если у вас возникнут проблемы с большими XML-документами, старайтесь избегать подхода DOM.

(require ['clojure.java.io :as 'io])
(import [javax.xml.parsers DocumentBuilderFactory])
(import [org.xml.sax InputSource])

(def dbf (DocumentBuilderFactory/newInstance))
(doto dbf
  (.setValidating false)
  (.setNamespaceAware true)
  (.setIgnoringElementContentWhitespace true))
(def builder (.newDocumentBuilder dbf))
(def doc (.parse builder (InputSource. (io/reader "C:/workspace/myproject/pom.xml"))))

(defn lazy-child-list [element]
  (let [nodelist (.getChildNodes element)
        len (.getLength nodelist)]
    (for [i (range len)]
      (.item nodelist i))))

;; To print the children of an element
(-> doc
    (.getDocumentElement)
    (lazy-child-list)
    (println))

;; Prints clojure.lang.LazySeq
(-> doc
    (.getDocumentElement)
    (lazy-child-list)
    (class)
    (println))
...