Question

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?

Was it helpful?

Solution

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top