title: Parse XML in Haskell
body: <p>I'm trying to get data from a webpage that serves a XML file periodically with stock market quotes (<a href="http://www.bmfbovespa.com.br/Pregao-Online/ExecutaAcaoAjax.asp?CodigoPapel=VALE3" rel="noreferrer">sample data</a>). The structure of the XML is very simple, and is something like this:</p>
  
  <pre><code>&lt;?xml version="1.0"?&gt;
  &lt;Contents&gt;
    &lt;StockQuote Symbol="PETR3" Date="21-12-2010" Time="13:20" Price="23.02" /&gt;
  &lt;/Contents&gt;
  </code></pre>
  
  <p>(it's more than that but this suffices as an example). </p>
  
  <p>I'd like to parse it to a data structure:</p>
  
  <pre><code> data Quote = Quote { symbol :: String, 
                        date   :: Data.Time.Calendar.Day, 
                        time   :: Data.Time.LocalTime.TimeOfDay,
                        price  :: Float}
  </code></pre>
  
  <p>I understand more or less how Parsec works (on the level of the Real World Haskell book), and I tried a bit the <code>Text.XML</code> library but all I could develop was a code that worked but is too big for such a simple task and looks like a half baked hack and not the best one could do. </p>
  
  <p>I don't know a lot about parsers and XML (I know basically what I read in the RWH book, I never used parsers before) (I just do statistical and numerical programming, I'm not a computer scientist). Is there a XML parsing library where I could just tell what is the model and extract the information right away, without having to parse each element by hand and without having to parse pure string?</p>
  
  <p>I'm thinking about something like: </p>
  
  <pre><code>  myParser = do cont  &lt;- openXMLElem "Contents"
                  quote &lt;- openXMLElem "StockQuote" 
                  symb &lt;- getXMLElemField "Symbol"
                  date &lt;- getXMLElemField "Date"
                  (...) 
                  closequote &lt;- closeXMLElem "StockQuote"
                  closecont  &lt;- closeXMLElem "Contents"
                  return (symb, date)
  
  
    results = parse myParser "" myXMLString
  </code></pre>
  
  <p>where I wouldn't have to deal with the pure string and create the combinators myself (I suck at it). </p>
  
  <p>EDIT: I probably need to read a bit (just enough to get this done the right way) about parsers in general (not only Parsec) and the minimum about XML. Do you guys recomend something? </p>
  
  <p>The real string I have to parse is this:</p>
  
  <pre><code> stringTest = "&lt;?xml version=\"1.0\"?&gt;\r\n&lt;ComportamentoPapeis&gt;&lt;Papel Codigo=\"PETR3\" 
   Nome=\"PETROBRAS ON\" Ibovespa=\"#\" Data=\"05/01/201100:00:00\" 
   Abertura=\"29,80\" Minimo=\"30,31\" Maximo=\"30,67\" Medio=\"30,36\" 
   Ultimo=\"30,45\" Oscilacao=\"1,89\" Minino=\"29,71\"/&gt;&lt;/ComportamentoPapeis&gt;\r\n"
  </code></pre>
  
  <p>EDIT2:</p>
  
  <p>I tried the following (readFloat, readQuoteTime, etc... are just functions to read things from strings). </p>
  
  <pre><code>bvspaParser :: (ArrowXml a) =&gt; a XmlTree Quote
  bvspaParser = hasName "ComportamentoPapeis" /&gt; hasName "Papel" &gt;&gt;&gt; proc x -&gt; do
     (hour,date) &lt;- readQuoteTime ^&lt;&lt; getAttrValue "Data" -&lt; x
     quoteCode   &lt;- getAttrValue "Codigo" -&lt; x
     openPrice   &lt;- readFloat ^&lt;&lt; getAttrValue "Abertura" -&lt; x
     minim       &lt;- readFloat ^&lt;&lt; getAttrValue "Minimo" -&lt; x
     maxim       &lt;- readFloat ^&lt;&lt; getAttrValue "Maximo" -&lt; x
     ultimo      &lt;- readFloat ^&lt;&lt; getAttrValue "Ultimo" -&lt; x
     returnA     -&lt; Quote quoteCode (LocalTime date hour) openPrice minim maxim ultimo
  
  docParser :: String -&gt; IO [Quote]
  docParser  str = runX $ readString [] str &gt;&gt;&gt; (parseXmlDocument False) &gt;&gt;&gt; bvspaParser
  </code></pre>
  
  <p>When I call it in ghci:</p>
  
  <pre><code>*Main&gt; docParser stringTest &gt;&gt;= print
  []
  </code></pre>
  
  <p>Is anything wrong?</p>