到目前为止,我看到的所有使用 Haskell XML 工具包 HXT 的示例都runX
用于执行解析器。runX
在 IO monad 内部运行。有没有办法在 IO 之外使用这个 XML 解析器?对我来说似乎是一个纯粹的操作,不明白为什么我被迫在 IO 里面。
2 回答
xread
具有以下类型:
xread :: ArrowXml a => a String XmlTree
这意味着您可以使用任何类型的箭头组合它(ArrowXml a) => a XmlTree Whatever
以获得a String Whatever
.
runLA
就像runX
,但对于类型的东西LA
:
runLA :: LA a b -> a -> [b]
LA
是 的一个实例ArrowXml
。
综上所述,我对您上一个问题的回答的以下版本使用 HXT 来解析包含格式良好的 XML 的字符串,而无需IO
涉及任何内容:
{-# LANGUAGE Arrows #-}
module Main where
import qualified Data.Map as M
import Text.XML.HXT.Arrow
classes :: (ArrowXml a) => a XmlTree (M.Map String String)
classes = listA (divs >>> pairs) >>> arr M.fromList
where
divs = getChildren >>> hasName "div"
pairs = proc div -> do
cls <- getAttrValue "class" -< div
val <- deep getText -< div
returnA -< (cls, val)
getValues :: (ArrowXml a) => [String] -> a XmlTree (String, Maybe String)
getValues cs = classes >>> arr (zip cs . lookupValues cs) >>> unlistA
where lookupValues cs m = map (flip M.lookup m) cs
xml = "<div><div class='c1'>a</div><div class='c2'>b</div>\
\<div class='c3'>123</div><div class='c4'>234</div></div>"
values :: [(String, Maybe String)]
values = runLA (xread >>> getValues ["c1", "c2", "c3", "c4"]) xml
main = print values
classes
并且getValues
与之前的版本相似,只是做了一些小的改动以适应预期的输入和输出。主要区别在于这里我们使用xread
andrunLA
而不是readString
and runX
。
能够以ByteString
类似的方式阅读像懒惰的东西会很好,但据我所知,目前 HXT 无法做到这一点。
其他几件事:您可以在没有 的情况下以这种方式解析字符串IO
,但尽可能使用它可能会更好runX
:它使您可以更好地控制解析器的配置、错误消息等。
另外:我试图使示例中的代码简单明了且易于扩展,但是如果您愿意,可以使用组合Control.Arrow
器Control.Arrow.ArrowList
更简洁地使用箭头。以下是 的等价定义classes
,例如:
classes = (getChildren >>> hasName "div" >>> pairs) >. M.fromList
where pairs = getAttrValue "class" &&& deep getText
Travis Brown 的回答非常有帮助。我只想在这里添加我自己的解决方案,我认为它更通用(使用相同的功能,只是忽略特定问题的问题)。
我以前不喜欢:
upIO :: XmlPickler a => String -> IO [a]
upIO str = runX $ readString [] str >>> arrL (maybeToList . unpickleDoc xpickle)
我可以改成这样:
upPure :: XmlPickler a => String -> [a]
upPure str = runLA (xreadDoc >>> arrL (maybeToList . unpickleDoc xpickle)) str
我完全同意他的观点,这样做可以减少对解析器等配置的控制,这是不幸的。