Skip to content

Commit

Permalink
Ensuring serialisation of XML root elements is total
Browse files Browse the repository at this point in the history
This is in regards to the case where xml-header styled services such as S3
should not have their actual request type considered for the root level element,
but instead use the singular nested field that is lifted into the body.

Fixes brendanhay#81.
  • Loading branch information
brendanhay committed Feb 21, 2015
1 parent dcf4d97 commit 9f3e455
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 23 deletions.
48 changes: 29 additions & 19 deletions core/src/Network/AWS/Data/Internal/XML.hs
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,20 @@ module Network.AWS.Data.Internal.XML
, element
, nodes
, (=@)
, extractRoot
, unsafeToXML
) where

import Control.Applicative
import Control.Monad
import Data.Default.Class
import Data.Maybe
import Data.Monoid
import Data.Text (Text)
import Network.AWS.Data.Internal.ByteString
import Network.AWS.Data.Internal.Text
import Numeric.Natural
import Text.XML
import Control.Applicative
import Control.Monad
import Data.Default.Class
import Data.Maybe
import Data.Monoid
import Data.Text (Text)
import Network.AWS.Data.Internal.ByteString
import Network.AWS.Data.Internal.Text
import Numeric.Natural
import Text.XML

decodeXML :: LazyByteString -> Either String [Node]
decodeXML = either failure success . parseLBS def
Expand All @@ -58,11 +59,11 @@ decodeXML = either failure success . parseLBS def
success = Right . elementNodes . documentRoot

encodeXML :: ToXMLRoot a => a -> LazyByteString
encodeXML x = renderLBS def d
encodeXML = maybe mempty (renderLBS def . d) . toXMLRoot
where
d = Document
d x = Document
{ documentPrologue = p
, documentRoot = toXMLRoot x
, documentRoot = x
, documentEpilogue = []
}

Expand Down Expand Up @@ -92,8 +93,8 @@ ns .@? n =
(.!@) :: Either String (Maybe a) -> a -> Either String a
f .!@ x = fromMaybe x <$> f

namespaced :: Text -> Text -> [Node] -> Element
namespaced g l = element (Name l (Just g) Nothing)
namespaced :: Text -> Text -> [Node] -> Maybe Element
namespaced g l = Just . element (Name l (Just g) Nothing)

element :: Name -> [Node] -> Element
element n = Element n mempty
Expand All @@ -104,9 +105,18 @@ nodes n ns = [NodeElement (element n ns)]
(=@) :: ToXML a => Name -> a -> Node
n =@ x = NodeElement (element n (toXML x))

extractRoot :: Text -> [Node] -> Maybe Element
extractRoot g ns =
case ns of
[NodeElement x] -> Just x { elementName = rename x }
_ -> Nothing
where
rename x = (elementName x) { nameNamespace = Just g }

-- | /Caution:/ This is for use with types which are 'flattened' in
-- AWS service model terminology. It is applied by the generator/templating
-- in safe contexts only.
-- AWS service model terminology.
--
-- It is applied by the generator/templating in safe contexts only.
unsafeToXML :: (Show a, ToXML a) => a -> Node
unsafeToXML x =
fromMaybe (error $ "Failed to unflatten node-list for: " ++ show x)
Expand Down Expand Up @@ -169,13 +179,13 @@ instance FromXML Double where parseXML = parseXMLText "Double"
instance FromXML Bool where parseXML = parseXMLText "Bool"

class ToXMLRoot a where
toXMLRoot :: a -> Element
toXMLRoot :: a -> Maybe Element

class ToXML a where
toXML :: a -> [Node]

default toXML :: ToXMLRoot a => a -> [Node]
toXML = (:[]) . NodeElement . toXMLRoot
toXML = maybeToList . fmap NodeElement . toXMLRoot

instance ToXML a => ToXML (Maybe a) where
toXML (Just x) = toXML x
Expand Down
12 changes: 8 additions & 4 deletions gen/templates/_include/xml-to-root.ede
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,18 @@ instance ToXML {{ type.name }} where
toXML = toXMLText
{% else %}
instance ToXMLRoot {{ type.name }} where
{% if type.contents | empty %}
{% if type.style %}
{% if type.contents | empty %}
toXMLRoot = const (namespaced ns "{{ type.name }}" [])
{% else %}
{% elif type.style == "xml-headers" && ((type.contents | length) == 1) %}
toXMLRoot = extractRoot ns {% for field in type.contents %}. toXML . {{ field.value.name }}{% endfor %}
{% else %}
toXMLRoot {{ type.name }}{..} = namespaced ns "{{ type.name }}"
{% for field in type.contents %}
{% for field in type.contents %}
{% include "list.ede" %} "{{ field.value.locationName | concat("\"") | justifyLeft(type.contentPad) }} =@ {{ field.value.name }}
{% endfor %}
{% endfor %}
]
{% endif %}
{% endif %}

instance ToXML {{ type.name }}
Expand Down

0 comments on commit 9f3e455

Please sign in to comment.