diff --git a/private/protocol/xml/xmlutil/build_test.go b/private/protocol/xml/xmlutil/build_test.go
index 0ab5b6866e5..a50900c857a 100644
--- a/private/protocol/xml/xmlutil/build_test.go
+++ b/private/protocol/xml/xmlutil/build_test.go
@@ -153,6 +153,12 @@ func TestBuildXML(t *testing.T) {
Input: &namedEmptyPayload{},
Expect: "",
},
+ "escape line feed and carriage return": {
+ Input: &implicitPayload{
+ StrVal: aws.String("this\nstring\rhas\r\nescapable\n\rcharacters"),
+ },
+ Expect: "this
string
has
escapable
characters",
+ },
}
for name, c := range cases {
diff --git a/private/protocol/xml/xmlutil/xml_to_struct.go b/private/protocol/xml/xmlutil/xml_to_struct.go
index 42f71648eee..2cf112a7e88 100644
--- a/private/protocol/xml/xmlutil/xml_to_struct.go
+++ b/private/protocol/xml/xmlutil/xml_to_struct.go
@@ -18,6 +18,14 @@ type XMLNode struct {
parent *XMLNode
}
+// textEncoder is a string type alias that implemnts the TextMarshaler interface.
+// This alias type is used to ensure that the line feed (\n) (U+000A) is escaped.
+type textEncoder string
+
+func (t textEncoder) MarshalText() (text []byte, err error) {
+ return []byte(t), nil
+}
+
// NewXMLElement returns a pointer to a new XMLNode initialized to default values.
func NewXMLElement(name xml.Name) *XMLNode {
return &XMLNode{
@@ -130,30 +138,35 @@ func StructToXML(e *xml.Encoder, node *XMLNode, sorted bool) error {
attrs = sortedAttrs
}
- e.EncodeToken(xml.StartElement{Name: node.Name, Attr: attrs})
+ startElement := xml.StartElement{Name: node.Name, Attr: attrs}
if node.Text != "" {
- e.EncodeToken(xml.CharData([]byte(node.Text)))
- } else if sorted {
- sortedNames := []string{}
- for k := range node.Children {
- sortedNames = append(sortedNames, k)
- }
- sort.Strings(sortedNames)
+ e.EncodeElement(textEncoder(node.Text), startElement)
+ } else {
+ e.EncodeToken(startElement)
- for _, k := range sortedNames {
- for _, v := range node.Children[k] {
- StructToXML(e, v, sorted)
+ if sorted {
+ sortedNames := []string{}
+ for k := range node.Children {
+ sortedNames = append(sortedNames, k)
}
- }
- } else {
- for _, c := range node.Children {
- for _, v := range c {
- StructToXML(e, v, sorted)
+ sort.Strings(sortedNames)
+
+ for _, k := range sortedNames {
+ for _, v := range node.Children[k] {
+ StructToXML(e, v, sorted)
+ }
+ }
+ } else {
+ for _, c := range node.Children {
+ for _, v := range c {
+ StructToXML(e, v, sorted)
+ }
}
}
+
+ e.EncodeToken(xml.EndElement{Name: node.Name})
}
- e.EncodeToken(xml.EndElement{Name: node.Name})
return e.Flush()
}