在Haskell中解析XML

Raf*_*ini 14 xml parsing haskell parsec

我正在尝试从定期提供XML文件的网页获取股票市场报价(样本数据).XML的结构非常简单,如下所示:

<?xml version="1.0"?>
<Contents>
  <StockQuote Symbol="PETR3" Date="21-12-2010" Time="13:20" Price="23.02" />
</Contents>
Run Code Online (Sandbox Code Playgroud)

(它不止于此,但这足以作为一个例子).

我想将它解析为数据结构:

 data Quote = Quote { symbol :: String, 
                      date   :: Data.Time.Calendar.Day, 
                      time   :: Data.Time.LocalTime.TimeOfDay,
                      price  :: Float}
Run Code Online (Sandbox Code Playgroud)

我或多或少地了解Parsec如何工作(在真实世界Haskell书的水平上),我尝试了一些Text.XML库,但我可以开发的是一个有效的代码,但对于这么简单的任务来说太大了,看起来像一个一半是黑客,而不是最好的.

我不知道了很多关于解析器和XML(我基本上知道我在RWH书中读到的东西,我从来没有使用解析器)(我只是做统计和数值编程,我不是一个计算机科学家).是否有一个XML解析库,我可以在其中告诉模型是什么并立即提取信息,而无需手工解析每个元素而无需解析纯字符串?

我正在考虑这样的事情:

  myParser = do cont  <- openXMLElem "Contents"
                quote <- openXMLElem "StockQuote" 
                symb <- getXMLElemField "Symbol"
                date <- getXMLElemField "Date"
                (...) 
                closequote <- closeXMLElem "StockQuote"
                closecont  <- closeXMLElem "Contents"
                return (symb, date)


  results = parse myParser "" myXMLString
Run Code Online (Sandbox Code Playgroud)

我不需要处理纯字符串并自己创建组合器(我很擅长).

编辑:我可能需要阅读一些关于解析器(不仅是Parsec)和关于XML的最小值的解释(刚刚以正确的方式完成).你们推荐一些东西吗?

我必须解析的真正字符串是这样的:

 stringTest = "<?xml version=\"1.0\"?>\r\n<ComportamentoPapeis><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\"/></ComportamentoPapeis>\r\n"
Run Code Online (Sandbox Code Playgroud)

EDIT2:

我尝试了以下(readFloat,readQuoteTime等...只是从字符串中读取东西的函数).

bvspaParser :: (ArrowXml a) => a XmlTree Quote
bvspaParser = hasName "ComportamentoPapeis" /> hasName "Papel" >>> proc x -> do
   (hour,date) <- readQuoteTime ^<< getAttrValue "Data" -< x
   quoteCode   <- getAttrValue "Codigo" -< x
   openPrice   <- readFloat ^<< getAttrValue "Abertura" -< x
   minim       <- readFloat ^<< getAttrValue "Minimo" -< x
   maxim       <- readFloat ^<< getAttrValue "Maximo" -< x
   ultimo      <- readFloat ^<< getAttrValue "Ultimo" -< x
   returnA     -< Quote quoteCode (LocalTime date hour) openPrice minim maxim ultimo

docParser :: String -> IO [Quote]
docParser  str = runX $ readString [] str >>> (parseXmlDocument False) >>> bvspaParser
Run Code Online (Sandbox Code Playgroud)

当我在ghci中调用它时:

*Main> docParser stringTest >>= print
[]
Run Code Online (Sandbox Code Playgroud)

有什么问题吗?

Ant*_*tti 19

有很多为Haskell编写的XML库可以为您解析.我推荐名为xml的库(参见http://hackage.haskell.org/package/xml).有了它,你可以简单地写例如:

let contents = parseXML source
    quotes   = concatMap (findElements $ simpleName "StockQuote") (onlyElems contents)
    symbols  = map (findAttr $ simpleName "Symbol") quotes
    simpleName s = QName s Nothing Nothing
print symbols
Run Code Online (Sandbox Code Playgroud)

此代码段打印[Just "PETR3"]为您的示例XML的结果,并且可以轻松扩展以收集所需的所有数据.要以您描述的样式编写程序,您应该使用Maybe monad,因为xml查找函数通常会返回一个Maybe String,表示是否可以找到标记,元素或属性.另请参阅相关问题:要使用哪个Haskell XML库?


scl*_*clv 5

对于简单的xml解析,你不能对tagsoup出错.http://hackage.haskell.org/package/tagsoup

  • @Michael - 如果我正在解析别人的烦人形式,我通常不在乎他们是否有正确的细节,或者我相信他们是否这样做取决于供应商的能力.我关心的是把我的信息拿出来,如果他们改变了我的话,我会非常谨慎. (3认同)

Mic*_*man 5

以下代码段使用 xml-enumerator。它将日期和时间作为文本(解析这些留给读者作为练习):

{-# LANGUAGE OverloadedStrings #-}
import Text.XML.Enumerator.Parse
import Data.Text.Lazy (Text, unpack)

data Quote = Quote { symbol :: Text
                   , date   :: Text
                   , time   :: Text
                   , price  :: Float}
  deriving Show

main = parseFile_ "test.xml" (const Nothing) $ parseContents

parseContents = force "Missing Contents" $ tag'' "Contents" parseStockQuote
parseStockQuote = force "Missing StockQuote" $ flip (tag' "StockQuote") return $ do
    s <- requireAttr "Symbol"
    d <- requireAttr "Date"
    t <- requireAttr "Time"
    p <- requireAttr "Price"
    return $ Quote s d t (read $ unpack p)
Run Code Online (Sandbox Code Playgroud)


eph*_*ent 4

我过去使用过Haskell XML Toolbox 。类似的东西

{-# LANGUAGE Arrows #-}

quoteParser :: (ArrowXml a) => a XmlTree Quote
quoteParser =
    hasName "Contents" /> hasName "StockQuote" >>> proc x -> do
    symbol <- getAttrValue "Symbol" -< x
    date <- readTime defaultTimeLocale "%d-%m-%Y" ^<< getAttrValue "Date" -< x
    time <- readTime defaultTimeLocale "%H:%M" ^<< getAttrValue "Time" -< x
    price <- read ^<< getAttrValue "Price" -< x
    returnA -< Quote symbol date time price

parseQuoteDocument :: String -> IO (Maybe Quote)
parseQuoteDocument xml =
    liftM listToMaybe . runX . single $
    readString [] xml >>> getChildren >>> quoteParser
Run Code Online (Sandbox Code Playgroud)