如何使用xml->获取嵌套节点在clojure.data.zip?

时间:2017-06-20 18:45:46

标签: xml clojure zipper

我找到了使用xml->非常混乱。我已经阅读了文档和示例,但无法弄清楚如何获取xml doc的嵌套节点。

假设以下xml在拉链中(从xml-zip开始):

<html>
 <body>
  <div class='one'>
    <div class='two'></div>
  </div>
 </body>
</html>

我正在尝试使用class =&#39; two&#39;返回div。

我期待这个工作:

(xml-> z :html :body :div :div)

或者这个:

(xml-> z :html :body :div (attr= :class "two"))

有点像css选择器。

但它只返回第一级,并且它不会向下搜索树。

我能让它发挥作用的唯一方法是:

(xml-> z :html :body :div children leftmost?)

这就是我应该做的吗?

我开始使用xml-&gt;的全部原因是为了方便,并避免上下左右拉链拉链。如果是xml->无法获得嵌套节点,然后我看不到clojure.zip上的值。

感谢。

2 个答案:

答案 0 :(得分:1)

两个相同的:div匹配同一个节点。你应该下来。 而且我相信你已经忘记了使用zip/node获得节点。

(ns reagenttest.sample
    (:require 
              [clojure.zip :as zip]
              [clojure.data.zip.xml :as data-zip]))
(let [s "..."
      doc (xml/parse (java.io.ByteArrayInputStream. (.getBytes s)))]
(prn (data-zip/xml-> (zip/xml-zip doc) :html :body :div zip/down (data-zip/attr= :class "two") zip/node)))

如果您对xml->不满意,可以使用自定义抽象:

(defn xml->find [loc & path]
    (let [new-path (conj (vec (butlast (interleave path (repeat zip/down)))) zip/node)]
        (apply (partial data-zip/xml-> loc) new-path)))

现在你可以这样做:

(xml->find z :html :body :div :div)
(xml->find z :html :body :div (data-zip/attr= :class "two"))

答案 1 :(得分:0)

您可以使用tupelo.forest from the Tupelo library解决此问题。 forest包含用于搜索和操作数据树的函数。就像Enlive对类固醇一样。以下是您的数据解决方案:

(dotest
  (with-forest (new-forest)
    (let [xml-str         "<html>
                             <body>
                               <div class='one'>
                                 <div class='two'></div>
                               </div>
                             </body>
                           </html>"

          enlive-tree     (->> xml-str
                            java.io.StringReader.
                            en-html/xml-resource
                            only)
          root-hid        (add-tree-enlive enlive-tree)

          ; Removing whitespace nodes is optional; just done to keep things neat
          blank-leaf-hid? (fn [hid] (ts/whitespace? (hid->value hid))) ; whitespace pred fn
          blank-leaf-hids (keep-if blank-leaf-hid? (all-leaf-hids)) ; find whitespace nodes
          >>              (apply remove-hid blank-leaf-hids) ; delete whitespace nodes found

          ; Can search for inner `div` 2 ways
          result-1        (find-paths root-hid [:html :body :div :div]) ; explicit path from root
          result-2        (find-paths root-hid [:** {:class "two"}]) ; wildcard path that ends in :class "two"
    ]
       (is= result-1 result-2) ; both searches return the same path
       (is= (hid->bush root-hid)
         [{:tag :html}
          [{:tag :body}
           [{:class "one", :tag :div}
            [{:class "two", :tag :div}]]]])
      (is=
        (format-paths result-1)
        (format-paths result-2)
        [[{:tag :html}
          [{:tag :body}
           [{:class "one", :tag :div}
            [{:class "two", :tag :div}]]]]])

       (is (val= (hid->elem (last (only result-1)))
             {:attrs {:class "two", :tag :div}, :kids []})))))

有许多示例in the unit teststhe forest-examples demo file