如何编写一个Enlive选择器来返回标签的“簇”?

时间:2013-05-04 17:01:59

标签: clojure enlive

我正在使用Enlive编写一些Clojure代码来处理一组XML文档。它们采用XML格式,大量借用HTML但添加了一些自定义标记,我的工作就是将它们转换为真正的HTML。现在困扰我的自定义标签是<tab>,它在各种不应该使用的地方使用。例如,它通常用于制作真正应该使用<ol><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> - 应该是 - <li>元素具有相同的属性class作为它们周围的<p>元素,有时会有两个连续的<p>组 - 应该是 - <li>元素,彼此具有相同的类(即,好像我发布的示例包含两个具有类ListWithTabs)的集群。我认为我可以依赖的一件事是,从来没有两个不同的列表,至少有一个非列表元素将它们分开:换句话说,任何具有属性的连续<p>元素的集群“具有至少有一个<tab>元素作为后代“都属于同一个列表。

考虑到这一点,我在REPL上进行了一些实验,在命名空间e下加载了Enlive(也就是说,(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-attake-while等任何功能。

感觉我非常接近,但却错过了最后一个关键步骤。那我该怎么做最后一步呢?如何仅选择与某些规则匹配的元素“群集”,但省略其他符合相同规则但不属于该第一个“群集”的元素?

1 个答案:

答案 0 :(得分:0)

根据有效文档:{node-selector node-selector}我们可以将它们与 {[:p.Normal] [:p.Normal]},假设这是分隔符。

现在,我的问题是:如何用enlive遍历每个结果组中的结果。