在haskell中从xml检索异构元素
我有一个大的.xml文件,我使用在haskell中从xml检索异构元素,xml,haskell,Xml,Haskell,我有一个大的.xml文件,我使用Text.xml.Light在Haskell中加载和解析它。文件中的元素表示不同的对象。有点像这样: <matchhistory> <game starttime="2/10/13 18:00" endtime="2/11/13 18:40"> <participant> <name>John Doe</name> <color>green</colo
Text.xml.Light
在Haskell中加载和解析它。文件中的元素表示不同的对象。有点像这样:
<matchhistory>
<game starttime="2/10/13 18:00" endtime="2/11/13 18:40">
<participant>
<name>John Doe</name>
<color>green</color>
</participant>
<participant>
<name>Jane Doe</name>
<color>blue</color>
</participant>
<winner color="blue">
</game>
<game starttime="2/11/13 17:00" endtime="2/11/13 17:30">
<participant>
<name>Jane Doe</name>
<color>green</color>
</participant>
<participant>
<name>John Doe</name>
<color>blue</color>
</participant>
<winner color="green">
</game>
...
</matchhistory>
我将如何执行此转换,分别遍历整个文件(26K行)以提取每种对象类型似乎不是很优雅?是否有一种很好的方法(可能是通过模式匹配来调用不同的构造函数)可以在一次扫描中完成此操作?是的,这将是构造此代码的一种很好的方法。使用您的数据定义,我能够非常轻松地找出如何将示例XML文档转换为数据结构。这里的技巧是构建增量解析器 首先,我们需要一些导入和数据定义的重述:
导入数据时间(UTCTime)
导入Data.Time.Format(parseTime)
导入System.Locale(defaultTimeLocale)
导入数据。可能(mapMaybe)
导入Text.XML.Light
导入控件。应用程序((),())
数据参与者=参与者
{name::String
,颜色::字符串
}推导(等式,显示)
数据游戏
{starttime::UTCTime
,endtime::UTCTime
,参加者::[参加者]
,winner::String
}推导(等式,显示)
数据匹配历史=匹配历史
{游戏::[游戏]
}推导(等式,显示)
我们可以使用Text.XML.Light
中的parseXML
函数将字符串转换为[Content]
。接下来,我们可以使用onlylems
提取所有顶级元素。对于解析,我们希望每个解析都能正常地失败,所以现在我们只使用Maybe
monad。我们可以为每种数据类型创建存根:
parseParticipant :: Element -> Maybe Participant
parseParticipant = undefined
parseGame :: Element -> Maybe Game
parseGame = undefined
parseMatchHistory :: Element -> Maybe MatchHistory
parseMatchHistory = undefined
这让我们可以编写一个文档解析器:
parseDocument :: String -> [MatchHistory]
parseDocument = mapMaybe parseMatchHistory . onlyElems . parseXML
现在,对于每个解析的实现:
parseParticipant pElem = Participant
<$> (strContent <$> findChild (blank_name { qName = "name" }) pElem)
<*> (strContent <$> findChild (blank_name { qName = "color" }) pElem)
最后,我们需要实现parseMatchHistory
。我把这个留给你做练习,但应该很容易
一旦你有了一个解析过的文档,你可以做如下的事情
averageTimePlayed :: MatchHistory -> DiffTime
averageTimePlayed = average . map calcDiff . games
where
average xs = sum xs / fromIntegral (length xs)
calcDiff g = endtime g `diffUTCTime` starttime g
虽然通过镜头的魔力,你可能会把它缩短。我喜欢
mapaybay
和parseXYZ
一起玩的方式。它导致了一个真正清晰简洁的实现。你对我问题的回答也是如此@j、 狗听了很高兴。你们当然可以在这个更干净的地方减少一点重复,我想象的更像
parseGame gElem = do
starttimeStr <- findAttrBy (("starttime" ==) . qName) gElem
endtimeStr <- findAttrBy (("endtime" ==) . qName) gElem
winnerElem <- findChild (blank_name { qName = "winner" }) gElem
let pElems = filterChildrenName (("participant" ==) . qName) gElem
...
parseTimeField :: String -> Maybe UTCTime
parseTimeField = parseTime defaultTimeLocale "%-m/%-d/%-y %R"
parseGame gElem = do
starttimeStr <- findAttrBy (("starttime" ==) . qName) gElem
endtimeStr <- findAttrBy (("endtime" ==) . qName) gElem
winnerElem <- findChild (blank_name { qName = "winner" }) gElem
let pElems = filterChildrenName (("participant" ==) . qName) gElem
Game <$> parseTimeField starttimeStr
<*> parseTimeField endtimeStr
<*> pure (mapMaybe parseParticipant pElems)
<*> findAttrBy (("color" ==) . qName) winnerElem
averageTimePlayed :: MatchHistory -> DiffTime
averageTimePlayed = average . map calcDiff . games
where
average xs = sum xs / fromIntegral (length xs)
calcDiff g = endtime g `diffUTCTime` starttime g