Clojure XML 拉链行走和修剪

Clojure XML zipper walk and prune

我正在走 html/xml 数据结构。我使用 clojure.zip 遍历它。一旦找到我想要 cut(修剪)的节点,我就无法找到删除所有 children 和正确节点的方法。

示例:

假设我有这棵树(代表 html):

(def tree [:p "F"
           [:p "G" [:p "I" [:p "H"]]]
           [:p "B"
            [:p
             "D"
             [:p "E"]
             [:p "C"]]
            [:p "A"]]])

我解析它,xml-zip它,在行走的某个时刻,我最终到达节点 "D",我想在该处剪切。我现在需要 return 没有 "E"、"C" (children) 和 "D" 的根。这些是此时使用 next 时尚未访问的所有节点。

如何删除这些节点?

注意:如果这不可行,我也欢迎将拉链复制到cut点的方法。

示例数据: 这是我对上述树的解析数据,我调用 xml-zip:

{:tag :html, :attrs nil, :content [{:tag :head, :attrs nil, :content nil} {:tag :body, :attrs nil, :content [{:tag :p, :attrs nil, :content ["F"]} {:tag :p, :attrs nil, :content ["G"]} {:tag :p, :attrs nil, :content ["I"]} {:tag :p, :attrs nil, :content ["H"]} {:tag :p, :attrs nil, :content nil} {:tag :p, :attrs nil, :content nil} {:tag :p, :attrs nil, :content ["B"]} {:tag :p, :attrs nil, :content ["D"]} {:tag :p, :attrs nil, :content ["E"]} {:tag :p, :attrs nil, :content ["C"]} {:tag :p, :attrs nil, :content nil} {:tag :p, :attrs nil, :content ["A"]} {:tag :p, :attrs nil, :content nil} {:tag :p, :attrs nil, :content nil}]}]}

我开始像这样浏览它以获取内容:

(-> parsed (z/xml-zip)
           (z/down) ;head
           (z/right) ; body
           (z/down) ; content
           )

另一个例子:

以下字符串:"<article><h1><img href=\"some-url\"></img> some-text <b>in bold</b></h1><ul><li> AA </li> <li>BB</li></ul></article>" 将给我以下地图:

[{:tag :html, :attrs nil, :content [{:tag :head, :attrs nil, :content nil} {:tag :body, :attrs nil, :content [{:tag :article, :attrs nil, :content [{:tag :h1, :attrs nil, :content [{:tag :img, :attrs {:href "some-url"}, :content nil} " some-text " {:tag :b, :attrs nil, :content ["in bold"]}]} {:tag :ul, :attrs nil, :content [{:tag :li, :attrs nil, :content [" AA "]} " " {:tag :li, :attrs nil, :content ["BB"]}]}]}]}]} nil]

在 "some-text" 处切割时,最终应生成字符串 <article><h1><img href=\"some-url\"></img> some-text</h1></article>

首先,我将按以下方式重新表述您的任务:

目标是找到某个节点,然后从它的 parent.

中删除它及其右侧的所有内容

这样说,cut 函数可以在 clojure.zip/edit 的帮助下很容易地实现,因为 parent:

(defn cut [loc]
  (when-let [parent (z/up loc)]
    (z/edit parent #(z/make-node loc % (z/lefts loc)))))

所以,如上所述,我们编辑 loc 的父节点,创建它的新节点,只保留 loc 左侧的子节点。

注意,那里有 when-let 宏,如果传递的位置没有父级(意味着它是拉链的根),可以避免空指针异常

现在测试:

让我们尝试删除包含 ["I"]:

p
user> (-> html
          z/xml-zip
          z/down
          z/right
          z/down
          z/right
          z/right
          z/node)
;; {:tag :p, :attrs nil, :content ["I"]}

user> (-> html
          z/xml-zip
          z/down
          z/right
          z/down
          z/right
          z/right
          cut
          z/root)
;;{:tag :html, :attrs nil, 
;; :content [{:tag :head, :attrs nil, :content nil} 
;;           {:tag :body, :attrs nil, 
;;            :content [{:tag :p, :attrs nil, :content ["F"]} 
;;                      {:tag :p, :attrs nil, :content ["G"]}]}]}

如预期:I 右侧(包括)的所有内容都已从正文中删除。

更新

根据更新,您想要删除树中的所有节点在目标节点之后。这有点棘手,因为它需要更改所有节点的父节点直到根节点。在这种情况下,cut 函数可能如下所示:

(defn cut [loc]
  (loop [loc loc]
    (if-let [parent (z/up loc)]
      (recur
       (z/replace parent
                  (z/make-node loc
                               (z/node parent)
                               (drop-last (count (z/rights loc))
                                          (z/children parent)))))
      (z/node loc))))

测试:

user> (-> h2 
          z/xml-zip 
          z/down 
          z/right 
          z/down 
          z/down 
          z/down 
          z/right 
          cut)

;;{:tag :html, :attrs nil, 
;; :content [{:tag :head, :attrs nil, :content nil} 
;;           {:tag :body, :attrs nil, 
;;            :content [{:tag :article, :attrs nil, 
;;                       :content [{:tag :h1, :attrs nil, 
;;                                  :content [{:tag :img, :attrs {:href "some-url"}, :content nil} " some-text "]}]}]}]}