Вы можете использовать HXT xread
вместе с runLA
для анализа строки XML вне IO
.
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
и 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