Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
147 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,70 +1,108 @@ | ||
{-# LANGUAGE OverloadedStrings #-} | ||
module Text.XML.ToJSON.Builder where | ||
module Text.XML.ToJSON.Builder | ||
( -- * Element type and operations | ||
Element(..) | ||
, emptyElement | ||
, addChild' | ||
, addValue' | ||
, addAttr' | ||
, addAttrs' | ||
-- * Stack type and operations | ||
, Stack | ||
, popStack | ||
, closeStack | ||
-- * Builder type and operations | ||
, Builder | ||
, runBuilder | ||
, beginElement | ||
, endElement | ||
, modifyTopElement | ||
, addChild | ||
, addValue | ||
, addAttr | ||
, addAttrs | ||
) where | ||
|
||
import Data.Text (Text) | ||
import Control.Monad.Trans.State | ||
|
||
type Str = Text | ||
|
||
-- | represent a XML element. | ||
data Element = Element | ||
{ elAttrs :: [(Str, Str)] | ||
, elValues :: [Str] | ||
, elChildren :: [(Str, Element)] | ||
{ elAttrs :: [(Text, Text)] -- ^ tag attributes. | ||
, elValues :: [Text] -- ^ text values. | ||
, elChildren :: [(Text, Element)] -- ^ child elements. | ||
} deriving (Show) | ||
|
||
emptyElement :: Element | ||
emptyElement = Element [] [] [] | ||
|
||
addChild' :: (Str, Element) -> Element -> Element | ||
-- | add a child element to an element | ||
addChild' :: (Text, Element) -> Element -> Element | ||
addChild' item o = o { elChildren = item : elChildren o } | ||
|
||
addValue' :: Str -> Element -> Element | ||
-- | add a text value to an element | ||
addValue' :: Text -> Element -> Element | ||
addValue' v o = o { elValues = v : elValues o } | ||
|
||
addAttr' :: (Str, Str) -> Element -> Element | ||
-- | add an attribute to an element | ||
addAttr' :: (Text, Text) -> Element -> Element | ||
addAttr' attr o = o { elAttrs = attr : elAttrs o } | ||
|
||
addAttrs' :: [(Str, Str)] -> Element -> Element | ||
-- | add multiple attributes to an element | ||
addAttrs' :: [(Text, Text)] -> Element -> Element | ||
addAttrs' as o = o { elAttrs = as ++ elAttrs o } | ||
|
||
type Stack = [(Str, Element)] | ||
type Builder = State Stack () | ||
|
||
runBuilder :: Builder -> Element | ||
runBuilder b = finishStack $ execState b [("", emptyElement)] | ||
-- | xml element stack with recent opened element at the top. | ||
type Stack = [(Text, Element)] | ||
|
||
-- | close current tag. | ||
popStack :: Stack -> Stack | ||
popStack ((k,v) : (name,elm) : tl) = (name, addChild' (k,v) elm) : tl | ||
popStack _ = error "popStack: can't pop root elmect." | ||
|
||
finishStack :: Stack -> Element | ||
finishStack [] = error "finishStack: empty stack." | ||
finishStack [(_, elm)] = elm | ||
finishStack st = finishStack (popStack st) | ||
-- | close all unclosed tags and return the root element. | ||
closeStack :: Stack -> Element | ||
closeStack [] = error "closeStack: empty stack." | ||
closeStack [(_, elm)] = elm | ||
closeStack st = closeStack (popStack st) | ||
|
||
-- | `Builder' is a `State' monad to transform a `Stack'. | ||
type Builder = State Stack () | ||
|
||
-- | exec the state monad and close the result stack. | ||
runBuilder :: Builder -> Element | ||
runBuilder b = closeStack $ execState b [("", emptyElement)] | ||
|
||
beginElement :: Str -> Builder | ||
-- | open element | ||
beginElement :: Text -> Builder | ||
beginElement name = | ||
modify ( (name, emptyElement) : ) | ||
|
||
-- | close element | ||
endElement :: Builder | ||
endElement = | ||
modify popStack | ||
|
||
-- | util to modify top element. | ||
modifyTopElement :: (Element -> Element) -> Builder | ||
modifyTopElement f = | ||
modify $ \st -> | ||
case st of | ||
((k, v) : tl) -> (k, f v) : tl | ||
_ -> fail "modifyTopElement: impossible: empty stack." | ||
|
||
addValue :: Str -> Builder | ||
-- | add value to top element. | ||
addValue :: Text -> Builder | ||
addValue = modifyTopElement . addValue' | ||
|
||
addAttr :: (Str, Str) -> Builder | ||
-- | add attribute to top element. | ||
addAttr :: (Text, Text) -> Builder | ||
addAttr = modifyTopElement . addAttr' | ||
|
||
addAttrs :: [(Str, Str)] -> Builder | ||
-- | add multiple attributes to top element. | ||
addAttrs :: [(Text, Text)] -> Builder | ||
addAttrs = modifyTopElement . addAttrs' | ||
|
||
addChild :: (Str, Element) -> Builder | ||
-- | add child element to top element. | ||
addChild :: (Text, Element) -> Builder | ||
addChild = modifyTopElement . addChild' |