2017-06-20 80 views
2

我找到了xml->非常混亂的用法。我已閱讀文檔和示例,但無法弄清楚如何獲取xml文檔的嵌套節點。如何在clojure.data.zip中使用xml->獲取嵌套節點?

假設以下XML是一個拉鍊(從XML-ZIP):

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

我試圖返回帶class = '二' 股利。

我期待這個工作:

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

或者這樣:

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

有點像CSS選擇器。

但它只返回第一級,並且不通過樹向下搜索。

我可以使它工作的唯一辦法是:

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

那是什麼,我該怎麼辦?

我開始使用xml->的全部原因是爲了方便起見,避免上下左右導航拉鍊。如果xml->無法獲得嵌套節點,那麼我看不到clojure.zip上的值。

謝謝。

回答

0

兩個連續的: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")) 
+0

我用zip/down的問題與我使用children:div的問題是一樣的。這是一個漏洞抽象。我希望能夠表達標籤的語義,即(xml-> z:html:body:div [2]文本)或類似的東西。瀏覽拉鍊SOMETIMES和使用標籤有時會令人困惑,並且閱讀不好。和我不想用(xml->:html children leftmost?)代替(xml->:html:body)一樣 – Scott

+1

我以前有過使用xml-seq的代碼,只是過濾並採用:content tags和I認爲xml->會簡單得多。但它實際上更復雜,而且由於這種不一致性,我認爲我會回到xml-seq,因爲這更容易理解。 – Scott

+0

看到我的更新。 – akond

1

可以解決使用tupelo.forest這個問題from the Tupelo libraryforest包含用於搜索和操作數據樹的功能。這就像激活類固醇。這裏是爲您的數據的解決方案:

(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