I have the following xml :
<list>
<recipient>
<name></name>
<lastname></lastname>
<email></email>
<phone></phone>
<home></home>
</recipient>
<recipient>
</recipient>
</list>
and the following data type :
data Recipient = Recipient { name :: String
, lastname :: String
, email :: String
, phone :: Maybe String
, home :: Maybe String }
What I want to do is read the xml and get back a list of recipients : [Recipient]
To do that I have written the following :
import Text.XML.HXT.Core
readMyXml :: FilePath -> IO [Recipient]
readMyXml path = do
-- lets read the doc
fe <- readFile path
let
-- parse it
doc = readString [withValidate no] fe
-- get all recipient nodes
reps <- getAllRep
-- the part I don't have
-- the only thing wrong in the following is the map function
-- I need an equivalent that runs on hxt trees
return $ map frmRep2Dat reps
-- ^
-- |
-- here
-- end of part I don't have
where
getAllRep = runX $ doc
>>> deep (hasName "list")
>>> multi (hasName "recipient")
frmRep2Dat branch = do
let
-- gets the recipient of a recipient node child
getV name = runX $
branch
>>> deep (hasName name)
>>> removeAllWhiteSpace
>>> deep getText
-- normaly there is no need to check because not maybe fields are
-- mandatory and should not be empty
getVal name = do
val <- getV name
case val of
[] -> ""
[""] -> ""
_ -> head val
-- some maybe wrapping
getMayVal name = do
val <- getV name
case val of
[] -> Nothing
[""] -> Nothing
_ -> Just (head val)
name <- getVal "name"
lastname <- getVal "lastname"
email <- getVal "email"
phone <- getMayVal "phone"
home <- getMayVal "home"
return $ Recipient name lastname email phone home
Any lead as to how to map the tree?
Found out that there is no need to iterate throught the tree. HXT already does it.
There is a simpler way of constructing data from xml than the naive one I wrote up.
I replaced the entire readMyXml function by :
readMyXml path = do
fi <- readFile path
let
doc = readString [withValidate no] fi
return =<< runX $ getRecipients doc
wrapStr a = if null a
then Nothing
else Just a
getD a = deep (hasName a)
>>> removeAllWhiteSpace
>>> deep getText
getMD a = getD a
>>^ wrapStr
getRecipients doc = doc
>>> deep (hasName "list")
>>> multi (hasName "recipient")
>>> proc y -> do
nime <- getD "name" -< y
lstn <- getD "lastname" -< y
mail <- getD "email" -< y
phon <- getMD "phone" -< y
homi <- getMD "home" -< y
returnA -< Recipient nime lstn mail phon homi
Now the return of getRecipients applied to the doc defined in the question is [Recipient]
Cheers