Skip to content

Use xml Marshal to encode attribute values in encodeOpenTag #152

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
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