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库?
对于简单的xml解析,你不能对tagsoup出错.http://hackage.haskell.org/package/tagsoup
以下代码段使用 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)
我过去使用过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)