我使用XML-conduit构建了一个GPX解析器,并且遇到了过于冗长和易碎的代码问题,这些代码用于识别元素和跳过不需要的标记。
识别元素(轻微的烦恼)
我只是通过比较nameLocalName
来明确忽略名称空间。我想正确的方法是将正确的命名空间硬编码到程序中并让帮助器构造我的元素名称以便在tag*
函数中进行比较?这有点令人讨厌,因为我必须支持至少两个不同的名称空间(GPX 1.1和1.0),这些名称空间非常相似,不需要为我的用途更改代码。
跳过元素
GPX较大,自定义扩展程序集较大。因为我正在构建的工具需要有限的信息,所以我决定忽略特定标签及其所有子元素。例如:
<trkpnt lat="45.19843" lon="-122.428">
<ele>4</ele>
<time>...</time>
<extensions>
...
</extensions>
</trkpnt>
要忽略具有众多子元素的extensions
和类似标签,我制作了一个接收器,使用元素直到结束元素Event
:
skipTagAndContents :: (MonadThrow m) => Text -> Sink Event m (Maybe ())
skipTagAndContents n = tagPredicate ((== n) . nameLocalName)
ignoreAttrs
(const $ many (skipElements n) >> return ())
skipElements t = do
x <- await
case x of
Just (EventEndElement n) | nameLocalName n == t -> Done x Nothing
Nothing -> Done x Nothing
_ -> return (Just ())
似乎应该有一个tag*
变体可以为我做这个(成功没有所有孩子被消耗)但事实上没有暗示我错过了一个简单的组合或者应该发送一个补丁 - 这是什么?
答案 0 :(得分:2)
如果您根本没有使用名称空间,最简单的方法是使用Data.Conduit.List.map stripNamespace
之类的内容完全删除它们。
坦率地说,我并不真正使用自己经常使用的流媒体界面;我的几乎所有工作都涉及DOM(Text.XML
)或游标接口。因此,完全有可能缺少组合器。但在这种情况下,我相信你可以大大简化实现,因为tagPredicate
不应该允许内部Sink
读取元素的结尾。因此,您可以将skipTagAndContents
重写为:
tagPredicate ((== n) . nameLocalName) ignoreAttrs (const Data.Conduit.List.sinkNull)
你应该在放入之前测试一下,我可能会错误地记住流媒体界面的一些细节。