{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}
{- |
Module : Text.XML.Decode
Description : Provides a Cursor based XML decoder that gives nice error messages
Copyright : (c) Ben Kolera
License : MIT
Maintainer : Ben Kolera
Stability : experimental
This library extends the functionality of 'Text.XML.Cursor' to allow you to
accumulate history as you traverse the structure. This leads to greater context
about why your decoder failed when it invariably does.
Here is an example of a decoder:
@
data BookCategory
= Haskell
| Scala
| Programming
| FunctionalProgramming
deriving (Eq,Show)
bookCategoryFromText :: Text -> Maybe BookCategory
bookCategoryFromText "Haskell" = Just Haskell
bookCategoryFromText "Scala" = Just Scala
bookCategoryFromText "Programming" = Just Programming
bookCategoryFromText "Functional Programming" = Just FunctionalProgramming
bookCategoryFromText _ = Nothing
data LibrarySection
= Fiction Text -- Fiction by author first letter(s)
| NonFiction Double -- Dewey decimal number
deriving (Eq,Show)
makePrisms ''LibrarySection
data Book = Book
{ _bookId :: Integer
, _bookName :: Text
, _bookSection :: LibrarySection
, _bookPublished :: Day
, _bookLastBorrowed :: Maybe UTCTime
, _bookCategories :: [BookCategory]
} deriving (Eq,Show)
makeLenses ''Book
data Library = Library
{ _books :: [Book]
} deriving (Eq,Show)
makeLenses ''Library
instance DecodeCursor BookCategory where
decode = parseCursor (parseMaybe "BookCategory" bookCategoryFromText)
instance DecodeCursor LibrarySection where
decode = decodeChoice
[ choice (laxElement "fiction") decodeFiction
, choice (laxElement "non_fiction") decodeNonFiction
]
where
decodeFiction c = Fiction <$> parseCursor parseText c
decodeNonFiction c = NonFiction <$> parseCursor parseDouble c
instance DecodeCursor Book where
decode c = Book
<$> decodeAttr "id" parseInteger c
<*> decodeSingle (c %/ laxElement "name")
<*> decodeSingle (c %/ laxElement "section")
<*> (decodeSingle (c %/ laxElement "published") <&> toDay)
<*> (decodeMay (c %/ laxElement "lastBorrowed") <&> fmap toUtcT)
<*> decodeMany (c %/ laxElement "category")
instance DecodeCursor Library where
decode c = Library
<$> decodeMany ( c %/ laxElement "book" )
@
If you tried to decode the following XML with a bad value for the section:
@
<library>
<book id="1">
<name>Learn you a haskell for great good!</name>
<section>
<reference>5.1</reference>
</section>
<published>2011-04-21</published>
<lastBorrowed>2015-01-05T16:30:00Z</lastBorrowed>
<category>Programming</category>
<category>Haskell</category>
<category>Functional Programming</category>
</book>
</library>
@
Then you'd get an error that looks like this:
@
Left (
"Choices Exhausted"
, [ MoveAxis Child , LaxElement "book"
, MoveAxis Child , LaxElement "section"
, Choice
[ [ MoveAxis Child , LaxElement "fiction" ]
, [ MoveAxis Child , LaxElement "non_fiction" ]]
Nothing
])
@
Checkout the tests for more examples!
-}
module Text.XML.Decode
( module Text.XML.Decode.DecodeCursor
, module Text.XML.Decode.HCursor
, module Text.XML.Decode.Parsers
, module Text.XML.Decode.Time
) where
import Text.XML.Decode.DecodeCursor
import Text.XML.Decode.HCursor
import Text.XML.Decode.Parsers
import Text.XML.Decode.Time