Data structures Clojure:如何将懒散的映射条目转换为structmap?
我是clojure的新手,一直在使用enlive转换html文档的文本节点。我的最终目标是将结构转换回html、标记和所有内容 我目前能够获取enlive html/html资源返回的structmap,并使用Data structures Clojure:如何将懒散的映射条目转换为structmap?,data-structures,recursion,clojure,enlive,Data Structures,Recursion,Clojure,Enlive,我是clojure的新手,一直在使用enlive转换html文档的文本节点。我的最终目标是将结构转换回html、标记和所有内容 我目前能够获取enlive html/html资源返回的structmap,并使用 (apply str (html/emit* nodes)) 其中节点是structmap 我还可以根据自己的意愿转换structmap的:content文本节点。但是,在转换structmap的内容文本节点之后,我最终得到了一个lazyseq的MapEntries。我想将其转换回st
(apply str (html/emit* nodes))
其中节点是structmap
我还可以根据自己的意愿转换structmap的:content文本节点。但是,在转换structmap的内容文本节点之后,我最终得到了一个lazyseq的MapEntries。我想将其转换回structmap,以便在其上使用emit*。这有点棘手,因为lazyseqs&structmap是嵌套的
tldr:
如何转换:
([:tag :html]
[:attrs nil]
[:content
("\n"
([:tag :head]
[:attrs nil]
[:content
("\n "
([:tag :title] [:attrs nil] [:content ("Page Title")])
" \n")])
"\n"
([:tag :body]
[:attrs nil]
[:content
("\n "
([:tag :div]
[:attrs {:id "wrap"}]
[:content
("\n "
([:tag :h1] [:attrs nil] [:content ("header")])
"\n "
([:tag :p] [:attrs nil] [:content ("some paragrah text")])
"\n ")])
"\n")])
"\n\n")])
进入:
更新
为我指出了中的更新方向,我可以使用该方向修改地图,而无需将其转换为序列,从而使我的问题变得无关紧要
(defn modify-or-go-deeper
"If item is a map, updates its content, else if it's a string, modifies it"
[item]
(declare update-content)
(cond
(map? item) (update-content item)
(string? item) (modify-text item)))
(defn update-content
"Calls modify-or-go-deeper on each element of the :content sequence"
[coll]
(update-in coll [:content] (partial map modify-or-go-deeper)))
我以前在地图上使用过for
,但是更新是一个不错的选择。试试看
(def mp '([:tag :html] [:attrs nil] [:content
(""
([:tag :head] [:attrs nil] [:content
("\n\t\t"
([:tag :title] [:attrs nil] [:content ("page title")])
"\n\t\t")])
"\n\t"
([:tag :body] [:attrs nil] [:content
("\n\t\t"
([:tag :div] [:attrs {:id "wrapper"}] [:content
("\n\t\t "
([:tag :h1] [:attrs nil] [:content
("\n \t\t\tpage title"
([:tag :br] [:attrs nil] [:content ()])
"\n \t\t\tand more title\n \t\t")])
"\n \t\t"
([:tag :p] [:attrs nil] [:content
("\n \t\tSome paragraph text"
([:tag :img] [:attrs {:src "images/image.png", :id "image"}] [:content nil])
"\n \t\t")])
"\n\t\t")]
"\n\t \n\t\t"))]
"\n\n"))]))
(clojure.walk/postwalk (fn [x]
(if (and (list? x) (vector? (first x)))
(into {} x)
x))
mp)
它将抛出一个错误,但是如果您将输入更改为
([:tag :html]
[:attrs nil]
[:content
(""
([:tag :head]
[:attrs nil]
[:content
("\n\t\t"
([:tag :title] [:attrs nil] [:content ("page title")])
"\n\t\t")])
"\n\t"
([:tag :body]
[:attrs nil]
[:content
("\n\t\t"
([:tag :div]
[:attrs {:id "wrapper"}]
[:content
("\n\t\t "
([:tag :h1]
[:attrs nil]
[:content
("\n \t\t\tpage title"
([:tag :br] [:attrs nil] [:content ()])
"\n \t\t\tand more title\n \t\t")])
"\n \t\t"
([:tag :p]
[:attrs nil]
[:content
("\n \t\tSome paragraph text"
([:tag :img]
[:attrs {:src "images/image.png", :id "image"}]
[:content nil])
"\n \t\t")])
"\n\t\t")]
))]))]))
那就行了。不同之处在于,在已编辑的输入中,您正在从包含键值对的同一列表中删除类似“\n\t\t”的字符串。希望这有帮助
编辑:
以下几点对我很有用:
(def mp '([:tag :html]
[:attrs nil]
[:content
(""
([:tag :head]
[:attrs nil]
[:content
("\n\t\t"
([:tag :title] [:attrs nil] [:content ("page title")])
"\n\t\t")])
"\n\t"
([:tag :body]
[:attrs nil]
[:content
("\n\t\t"
([:tag :div]
[:attrs {:id "wrapper"}]
[:content
("\n\t\t "
([:tag :h1]
[:attrs nil]
[:content
("\n \t\t\tpage title"
([:tag :br] [:attrs nil] [:content ()])
"\n \t\t\tand more title\n \t\t")])
"\n \t\t"
([:tag :p]
[:attrs nil]
[:content
("\n \t\tSome paragraph text"
([:tag :img]
[:attrs {:src "images/image.png", :id "image"}]
[:content nil])
"\n \t\t")])
"\n\t\t")]
))]))]))
(clojure.walk/postwalk (fn [x]
(if (and (list? x) (vector? (first x)))
(into {} x)
x))
mp)
尝试复制并粘贴到repl中。您应该获得以下信息:
{:tag :html,
:attrs nil,
:content
(""
{:tag :head,
:attrs nil,
:content
("\n\t\t"
{:tag :title, :attrs nil, :content ("page title")}
"\n\t\t")}
"\n\t"
{:tag :body,
:attrs nil,
:content
("\n\t\t"
{:tag :div,
:attrs {:id "wrapper"},
:content
("\n\t\t "
{:tag :h1,
:attrs nil,
:content
("\n \t\t\tpage title"
{:tag :br, :attrs nil, :content ()}
"\n \t\t\tand more title\n \t\t")}
"\n \t\t"
{:tag :p,
:attrs nil,
:content
("\n \t\tSome paragraph text"
{:tag :img,
:attrs {:src "images/image.png", :id "image"},
:content nil}
"\n \t\t")}
"\n\t\t")})})}
只需将所有内容放回地图,然后递归地遍历内容
(defn into-xml
[coll]
(let [tag (into {} coll)]
(update-in tag [:content] (partial map into-xml))))
请注意,仅当您访问内容时才会对其进行转换
编辑:Woops,错过了字符串部分。这里有一个工作版本:
(defn into-xml
[coll]
(if-not (string? coll)
(let [tag (into {} coll)]
(update-in tag [:content] (partial map into-xml)))
coll))
java.lang.RuntimeException:java.lang.RuntimeException:java.lang.RuntimeException:java.lang.RuntimeException:java.lang.ClassCastException:java.lang.Character无法强制转换为java.util.Map$EntryHey,我实际上没有收到错误,因此可能是我粘贴的输入不正确。但这似乎根本没有改变集合,它仍然返回相同的lazyseq MapEntries。就像我说的,我是clojure的新手,所以我不确定我是否正确使用了它,但我把它放入了一个函数中,比如:(defn-retransform[mp](clojure.walk/postwark(fn[x](if(and(list?x)(vector?(first x))(into{}x)mp))
嗯,这对我来说很有效。我已经更新了我的帖子,上面有一个例子,你可以直接复制粘贴。谢谢。这似乎很管用,但一旦进入内容序列就会出错。将我的集合传递到此函数中,我得到:{:tag:html,:attrs nil,:content(IllegalArgumentException不知道如何从以下位置创建ISeq:java.lang.Character clojure.lang.RT.seqFrom(RT.java:487)
我现在正在使用你的解决方案,看看我是否能弄清楚发生了什么。嘿,正如我所说,我是clojure的新手。你的解决方案为我指明了在
中更新的方向,我可以在原始集合中使用它,而不是在
中使用,因此我保留了地图结构,而不是转换为MapEntries的序列。我在问题的最后给出了我如何遍历集合的解决方案。谢谢!@jmw啊,是的。字符串。我添加了一个固定版本,但您的解决方案当然更好,首先使用更新在中。不过请注意,声明应该在顶层。对在实践中,我会敦促读者使用trampoline
,避免相互递归的陷阱。。。
(defn into-xml
[coll]
(if-not (string? coll)
(let [tag (into {} coll)]
(update-in tag [:content] (partial map into-xml)))
coll))