我正在使用 Enlive 编写一些 Clojure 代码来处理一组 XML 文档。它们采用 XML 格式,大量借鉴了 HTML,但添加了一些自定义标签,我的工作是将它们转换为真正的 HTML。现在最困扰我的自定义标签是<tab>
,它被用于各种不应该使用的地方。例如,它通常用于制作真正应该用<ol>
and制作的列表<li>
。这是我遇到的那种事情的一个例子:
<p class="Normal">Some text</p>
<p class="ListWithTabs">(a)<tab />First list item</p>
<p class="ListWithTabs">(b)<tab />Second list item</p>
<p class="ListWithTabs">(c)<tab />Third list item</p>
<p class="Normal">Some more text</p>
<p class="AnotherList">1.<tab />Another list</p>
<p class="AnotherList">2.<tab />Two items this time</p>
<p class="Normal">Some final text</p>
我想把它变成:
<p class="Normal">Some text</p>
<ol type="a">
<li class="ListWithTabs">First list item</li>
<li class="ListWithTabs">Second list item</li>
<li class="ListWithTabs">Third list item</li>
</ol>
<p class="Normal">Some more text</p>
<ol type="1">
<li class="AnotherList">Another list</li>
<li class="AnotherList">Two items this time</li>
</ol>
<p class="Normal">Some final text</p>
为此,我需要获取<p>
包含<tab>
后代的元素(使用 Enlive 选择器很容易),并根据它们在原始 XML 文档中的自然分组以某种方式对它们进行聚类(要困难得多)。
我查看了文档并确定我不能依赖该class
属性:有时这些<p>
-that-should-be-<li>
元素与它们周围的元素具有相同的类<p>
,有时有两组连续的<p>
-that-应该<li>
是彼此具有相同类的元素(即,好像我发布的示例中的两个集群都具有类ListWithTabs
)。我认为我可以依赖的一件事是,如果没有至少一个非列表元素将它们分开,就永远不会有两个不同的列表:换句话说,任何<p>
具有属性“至少有一个<tab>
元素作为后代”都是同一个列表的一部分。
考虑到这一点,我在 REPL 上做了一些实验,在命名空间下加载了 Enlive e
(也就是说,(require '[net.cgrand.enlive-html :as e])
应该假设对我的所有其余问题都有效)。编写一个选择器来挑选我想要的元素很容易,但(e/select snippet [(e/has [:tab])])
会返回一个包含 5 个元素的列表(嗯,这确实是一个惰性序列)。但我想要的是一个列表列表:第一个包含三个元素,第二个包含两个。像这样模糊的东西(请原谅非标准的缩进):
[
[{:tag :p, :content (... "First list item" ...)}
{:tag :p, :content (... "Second list item" ...)}
{:tag :p, :content (... "Third list item" ...)}
] ; 3 items in first list
[{:tag :p, :content (... "Another list" ...)}
{:tag :p, :content (... "With just two items" ...)}
] ; 2 items in second list
]
我能够创建以下选择器:
(def first-of-tab-group [(e/has [:tab])
(e/left (complement (e/has [:tab])))])
(def rest-of-tab-group [(e/has [:tab])
(e/left (e/has [:tab]))])
但现在我被困住了。我想做类似的事情(e/select snippet [[(e/start-at first-of-tab-group) (e/take-while rest-of-tab-group)]])
,但据我所知,Enlive 没有像start-at
and这样的功能take-while
。
感觉就像我非常接近,但只是错过了最后一个关键步骤。那么我该如何迈出最后一步呢?如何只选择与某些规则匹配的元素“集群”,但忽略其他符合相同规则但不属于第一个“集群”的元素?