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 "]}]}]}]}