在 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
我想将嵌套级别不是很深的有效 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