hexml-lens-0.2.1: examples/courses.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PartialTypeSignatures #-}
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
{-# LANGUAGE DisambiguateRecordFields #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS -Wno-unused-top-binds #-}
import Control.Applicative (optional)
import qualified Control.Category as C
import Control.Lens hiding (children)
import qualified Data.ByteString.Lazy.Char8 as LB
import Network.Wreq
import Test.Hspec
import Text.XML.Hexml
import Text.XML.Hexml.Lens
url,url2 :: String
url = "http://aiweb.cs.washington.edu/research/projects/xmltk/xmldata/data/courses/reed.xml"
url2 = "http://aiweb.cs.washington.edu/research/projects/xmltk/xmldata/data/courses/uwm.xml"
type Strings = String
data Place s = Place
{ _building :: s
, _room :: Int
}
deriving Show
data Course s = Course
{ _title :: s
, _courseInstructor :: Maybe s
, _place :: Maybe (Place s)
, _courseSections :: [Section s]
}
deriving Show
data Section s = Section
{ _name :: s
, _days :: s
, _start, _end :: s
, _instructor :: s
}
deriving Show
makeFields ''Place
makeFields ''Course
makeFields ''Section
main :: IO ()
main = do
r <- get url
r2 <- get url2
let parser :: Fold (Response LB.ByteString) (Course Strings)
parser = responseBody . to stripDocTypeB . _XML . _children . folded . courseF
lengthOf parser r `shouldBe` 2510
lengthOf parser r2 `shouldBe` 2112
r `shouldSatisfy` allOf (parser.sections) null
r2 `shouldSatisfy` allOf (parser.sections) (not.null)
stripDocTypeB :: LB.ByteString -> LB.ByteString
stripDocTypeB = LB.unlines . drop 2 . LB.lines
courseF :: Fold Node (Course Strings)
courseF = runFold $ do
_title <- field "title"
_courseInstructor <- optional $ field "instructor"
_place <- optional $ Fold $ node "place" . placeF
_courseSections <- Fold $ multiple $ node "section_listing" . sectionF
return Course{..}
sectionF :: Fold Node (Section Strings)
sectionF = runFold $ do
_instructor <- field "instructor"
_name <- field "section"
_days <- field "days"
(_start, _end) <- Fold $ node "hours" . runFold hoursF
return Section{..}
hoursF :: ReifiedFold Node (Strings, Strings)
hoursF = (,) <$> field "start" <*> field "end"
placeF :: Fold Node (Place Strings)
placeF = runFold $ do
building <- field "building"
room <- Fold _Show C.. field "room"
return (Place building room)
field :: (XML a, XML t) => t -> ReifiedFold Node a
field n = Fold $ node n . _inner