在 Haskell 中转换 HTML

Transform HTML in Haskell

我想将嵌套级别不是很深的有效 HTML 转换为另一个具有更多限制规则的 HTML。

结果 HTML 中仅支持以下标签:

<b></b>, <strong></strong>, <i></i>, <em></em>, <a
href="URL"></a>, <code></code>, <pre></pre>

完全不允许嵌套标签。

对于其余的标签及其组合,我必须创建某种规则来处理每个标签。 所以我必须转换为:

<p>text</p> 转换为带有换行符的简单字符串 text

<b>text <a href="url">link</a> text</b> 变成 text link text

<a href="url">text<code> code here</code></a> 变成 <a href="url">text code here</a> 因为 <code> 嵌套在 <a> 中,依此类推。

例如HTML(换行只是为了方便):

<p>long paragraph <a href="url">link</a> </p>
<p>another text <pre><code>my code block</code></pre> the rest of description</p>
<p><code>inline monospaced text with <a href="url">link</a></code></p>

应转化为:

long paragraph <a href="url">link</a>

another text <code>my code block</code> the rest of description

<code>inline monospaced text with link</code>

有什么解决方法的建议吗?

经过一些调查,我找到了一个在我看来非常优雅的解决方案。它基于 tagsoup 库。该库有 Text.HTML.TagSoup.Tree 模块,有助于将 HTML 解析为树结构。

它还包含 transformTree 函数,可以进行非常简单的转换。该功能的文档说:

This operation is based on the Uniplate transform function. Given a list of trees, it applies the function to every tree in a bottom-up manner.

您可以阅读更多有关 Uniplate 的内容 here

这是我满意的代码:

import Text.HTML.TagSoup
import Text.HTML.TagSoup.Tree

convert = transformTree f
    where
      f (TagLeaf (TagOpen "br" _)) = [TagLeaf (TagText "\n")] -- line breaks
      f (TagLeaf (TagOpen _ _)) = [] -- ignore all tags without closing pairs
      f (TagBranch "a" attrs inner) = tagExtr "a" attrs inner -- keeps href for <a>
      f (TagBranch "p" _ inner) = inner ++ [(TagLeaf (TagText "\n"))]
      f (TagBranch "pre" _ [TagBranch "code" _ inner]) = tagExtr "pre" [] inner -- <pre><code> -> <code>
      f (TagBranch tag _ inner) = if tag `elem` allowedTags then tagExtr tag [] inner else inner
      f x = [x]

tagExtr tag attrs inner = [TagBranch tag attrs [(extractFrom inner)]]

allowedTags = ["b", "i", "a", "code", "a", "pre", "em", "strong"]

extractFrom x = TagLeaf $ TagText $ (innerText . flattenTree) x