到目前为止,我使用Haskell XML工具包HXT看到的所有示例都使用runX
来执行解析器。 runX
在IO monad中运行。有没有办法在IO之外使用这个XML解析器?对我来说似乎是一个纯粹的操作,不明白为什么我被迫进入IO。
答案 0 :(得分:28)
您可以使用HXT的xread
和runLA
来解析IO
之外的XML字符串。
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
的实例。
要将所有内容放在一起,以前版本的my answer使用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
和runLA
代替readString
和runX
。
能够以类似的方式阅读类似懒惰ByteString
的内容会很高兴,但据我所知,HXT目前无法实现这一目标。
其他一些事情:你可以以这种方式解析字符串而不用IO
,但是最好随时使用runX
:它可以让你获得更多控制权解析器的配置,错误消息等
另外:我尝试使示例中的代码简单易用,但Control.Arrow
和Control.Arrow.ArrowList
中的组合使得如果您愿意,可以更简洁地使用箭头。以下是classes
的等效定义,例如:
classes = (getChildren >>> hasName "div" >>> pairs) >. M.fromList
where pairs = getAttrValue "class" &&& deep getText
答案 1 :(得分:1)
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
我完全同意他的说法,这样做可以减少对解析器等配置的控制,这是不幸的。