4

我使用XML-conduit构建了一个 GPX 解析器,并且遇到了用于识别元素和跳过不需要的标签的代码过于冗长和脆弱的问题。

识别元素(一个小烦恼)

我通过仅比较nameLocalNames 来明确忽略名称空间。我想正确的方法是将正确的命名空间硬编码到程序中,并让一个助手构造我的元素名称以便在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*变体可以为我做到这一点(在没有消耗所有孩子的情况下成功)但是没有表明我缺少一个简单的组合器或应该发送一个补丁的事实 - 是它吗?

4

1 回答 1

2

如果您根本不使用命名空间,最简单的方法可能是完全剥离它们,使用类似Data.Conduit.List.map stripNamespace.

坦率地说,我自己并不经常使用流媒体接口。我几乎所有的工作都涉及到 DOM ( Text.XML) 或游标接口。所以完全有可能缺少组合子。但是在这种情况下,我相信您可以大大简化实现,因为tagPredicate不应该让内部Sink读取超过元素的末尾。所以你可以重写skipTagAndContents为:

tagPredicate ((== n) . nameLocalName) ignoreAttrs (const Data.Conduit.List.sinkNull)

您应该在将其放入之前对其进行测试,我可能会错误地记住流接口的一些细节。

于 2012-06-04T03:44:12.087 回答