Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions xmltree/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ func ExampleMarshal() {
<number>2</number>
</chapter>
<chapter>
<title>A Good Going-over.Grace Triumphant."One of Tom Sawyers's Lies".</title>
<title>A Good Going-over.Grace Triumphant.&quot;One of Tom Sawyers's Lies&quot;.</title>
<number>3</number>
</chapter>
<chapter>
Expand Down Expand Up @@ -220,7 +220,7 @@ func ExampleMarshal() {
// <toc>
// <chapter>Civilizing Huck.Miss Watson.Tom Sawyer Waits.</chapter>
// <chapter>The Boys Escape Jim.Torn Sawyer's Gang.Deep-laid Plans.</chapter>
// <chapter>A Good Going-over.Grace Triumphant."One of Tom Sawyers's Lies".</chapter>
// <chapter>A Good Going-over.Grace Triumphant.&quot;One of Tom Sawyers's Lies&quot;.</chapter>
// <chapter>Huck and the Judge.Superstition.</chapter>
// </toc>
}
Expand Down
83 changes: 81 additions & 2 deletions xmltree/marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"encoding/xml"
"io"
"strings"
"text/template"
)

Expand All @@ -22,6 +23,48 @@ var tagTmpl = template.Must(template.New("Marshal XML tags").Parse(
{{define "end" -}}
</{{.Prefix .Name}}>{{end}}`))

type vContentMapping struct {
Decoded string
Encoded string
}

var vContentMappings = []vContentMapping{
{Decoded: `&`, Encoded: `&amp;`},
{Decoded: `<`, Encoded: `&lt;`},
{Decoded: `>`, Encoded: `&gt;`},
{Decoded: `"`, Encoded: `&quot;`},
}

// XML encode any special characters in a plain string.
// For example & will be encoded as &amp;

func xmlEncodeString(strToEncode string) (string, error) {
strEncoded := strToEncode

for _, mapping := range vContentMappings {
strEncoded = strings.Replace(strEncoded, mapping.Decoded, mapping.Encoded, -1)
}

//fmt.Printf("xmlEncodeString([%s]) -> [%s]\n", strToEncode, strEncoded)

return strEncoded, nil
}

// XML decode escaped characters in a string.
// For example &quot; will be encoded as "

func xmlDecodeString(strToDecode string) (string, error) {
strDecoded := strToDecode

for _, mapping := range vContentMappings {
strDecoded = strings.Replace(strDecoded, mapping.Encoded, mapping.Decoded, -1)
}

//fmt.Printf("xmlDecodeString([%s]) -> [%s]\n", strToDecode, strDecoded)

return strDecoded, nil
}

// Marshal produces the XML encoding of an Element as a self-contained
// document. The xmltree package may adjust the declarations of XML
// namespaces if the Element has been modified, or is part of a larger scope,
Expand Down Expand Up @@ -96,7 +139,11 @@ func (e *encoder) encode(el, parent *Element, visited map[*Element]struct{}) err
}
if len(el.Children) == 0 {
if len(el.Content) > 0 {
e.w.Write(el.Content)
mStr, mErr := xmlEncodeString(string(el.Content))
if mErr != nil {
return mErr
}
e.w.Write([]byte(mStr))
} else {
return nil
}
Expand Down Expand Up @@ -139,10 +186,42 @@ func (e *encoder) encodeOpenTag(el *Element, scope Scope, depth int) error {
io.WriteString(e.w, e.indent)
}
}
// Note that a copy of el is used here so that XML encoded attributes are generated
var elCopy *Element = &Element{}
elCopy.StartElement = xml.StartElement{}
elCopy.StartElement.Name = el.StartElement.Name
elCopy.StartElement.Attr = make([]xml.Attr, len(el.StartElement.Attr))
for i := 0; i < len(el.StartElement.Attr); i++ {
elCopy.StartElement.Attr[i] = el.StartElement.Attr[i]
}
elCopy.Scope = el.Scope
// Escape node contents
{
mStr, mErr := xmlEncodeString(string(el.Content))
if mErr != nil {
return mErr
}
elCopy.Content = []byte(mStr)
}
elCopy.Children = el.Children

var tag = struct {
*Element
NS []xml.Name
}{Element: el, NS: scope.ns}
}{Element: elCopy, NS: scope.ns}

// XML escape attribute strings held in copy
attrs := tag.StartElement.Attr
for i := 0; i < len(attrs); i++ {
attrStr := attrs[i].Value
mStr, mErr := xmlEncodeString(attrStr)
if mErr != nil {
return mErr
}
attrs[i].Value = mStr
}
tag.StartElement.Attr = attrs

if err := tagTmpl.ExecuteTemplate(e.w, "start", tag); err != nil {
return err
}
Expand Down
Loading