Clojure XML молния ходить и обрезать

Я хожу структуру данных HTML / XML. Я иду через это, используя clojure.zip, Однажды я нахожу узел, на котором я хочу cut (обрезать), я не могу найти способ удалить все дочерние и правые узлы.

Пример:

Допустим, у меня есть это дерево (которое представляет HTML):

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

Я разбираю это, xml-zip это, и в какой-то момент во время ходьбы я оказываюсь в узле "D", где я хочу резать. Теперь мне нужно вернуть рут без "E", "C" (дети) и "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]

при вырезании на "некоторый текст" это должно в конечном итоге привести к строке <article><h1><img href=\"some-url\"></img> some-text</h1></article>

1 ответ

Решение

Во-первых, я бы перефразировал вашу задачу следующим образом:

Цель состоит в том, чтобы найти какой-то узел, а затем удалить его и все справа от него от его родителя.

Заявленный таким образом, cut Функция может быть легко реализована с помощью clojure.zip/edit для родителя:

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

поэтому, как было сказано выше, мы редактируем родительский элемент locсоздавая его новый узел, оставляя только дочерние элементы слева от loc,

обратите внимание, что есть when-let макрос, чтобы избежать исключения нулевого указателя, если переданное местоположение не имеет родителя (то есть это корень молнии)

Теперь тест:

давайте попробуем удалить p содержащий ["I"]:

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 "]}]}]}]}
Другие вопросы по тегам