module Aws.Query.Types (
Value(..)
, XMLValueOptions(..)
, toValue
, castValue
) where
import Data.Text (Text)
import qualified Data.Text as T
import Data.Aeson
import Data.Aeson.Types (parseMaybe)
import qualified Data.Vector as V
import qualified Data.List as L
import Text.XML (Element(..), Name(..), Node(..))
import Aws.Core (AsMemoryResponse, MemoryResponse(..))
data XMLValueOptions = XMLValueOptions
{ arrayElementTag :: Text
}
instance AsMemoryResponse Value where
type MemoryResponse Value = Value
loadToMemory = return
traceArg = id
castValue :: FromJSON a => Value -> Maybe a
castValue v = parseMaybe (const (parseJSON v)) v
toValue :: XMLValueOptions -> Node -> Value
toValue = value
value :: XMLValueOptions -> Node -> Value
value options (NodeElement e@Element{..}) = values options elementNodes
value options (NodeContent c) = String c
value options _ = Null
values options elementNodes = uncurry (elementValues options) $ traceArg $ (elementKind options elementNodes, elementNodes)
data ElementKind = ObjectLike
| ArrayLike
| Other
deriving (Show)
elementKind XMLValueOptions{..} nodes
| isXMLArray = ArrayLike
| isObject = ObjectLike
| otherwise = Other
where
filtered = filterNodes nodes
elems = onlyElements nodes
isObject = (not $ null elems) && length filtered == length elems
isXMLArray = [arrayElementTag] == (L.nub $ fmap forceElementName elems)
elementValues :: XMLValueOptions -> ElementKind -> [Node] -> Value
elementValues options ObjectLike ns = object [(forceElementName n, values options $ filterNodes $ elementNodes $ unElement n) | n <- onlyElements ns]
elementValues options ArrayLike ns = array $ innerNodes ns
where
innerNodes :: [Node] -> [Value]
innerNodes nodes = fmap (values options . filterNodes . elementNodes . unElement) $ onlyElements $ filterNodes $ nodes
elementValues options Other ns = arrayOrValue $ fmap (value options) ns
where
arrayOrValue (x:[]) = x
arrayOrValue [] = Null
arrayOrValue a = array a
forceElementName = nameLocalName . elementName . unElement
unElement (NodeElement e) = e
array = Array . V.fromList
onlyElements :: [Node] -> [Node]
onlyElements = filter $ \case
NodeElement _ -> True
_ -> False
filterNodes :: [Node] -> [Node]
filterNodes = filter $ \case
NodeContent s -> (T.strip s) /= T.empty
_ -> True