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