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?

Était-ce utile?

La 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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top