summaryrefslogtreecommitdiff
path: root/src/pkg/encoding/xml/marshal.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/encoding/xml/marshal.go')
-rw-r--r--src/pkg/encoding/xml/marshal.go75
1 files changed, 74 insertions, 1 deletions
diff --git a/src/pkg/encoding/xml/marshal.go b/src/pkg/encoding/xml/marshal.go
index ea58ce254..47b001763 100644
--- a/src/pkg/encoding/xml/marshal.go
+++ b/src/pkg/encoding/xml/marshal.go
@@ -120,11 +120,76 @@ func (enc *Encoder) Encode(v interface{}) error {
type printer struct {
*bufio.Writer
+ seq int
indent string
prefix string
depth int
indentedIn bool
putNewline bool
+ attrNS map[string]string // map prefix -> name space
+ attrPrefix map[string]string // map name space -> prefix
+}
+
+// createAttrPrefix finds the name space prefix attribute to use for the given name space,
+// defining a new prefix if necessary. It returns the prefix and whether it is new.
+func (p *printer) createAttrPrefix(url string) (prefix string, isNew bool) {
+ if prefix = p.attrPrefix[url]; prefix != "" {
+ return prefix, false
+ }
+
+ // The "http://www.w3.org/XML/1998/namespace" name space is predefined as "xml"
+ // and must be referred to that way.
+ // (The "http://www.w3.org/2000/xmlns/" name space is also predefined as "xmlns",
+ // but users should not be trying to use that one directly - that's our job.)
+ if url == xmlURL {
+ return "xml", false
+ }
+
+ // Need to define a new name space.
+ if p.attrPrefix == nil {
+ p.attrPrefix = make(map[string]string)
+ p.attrNS = make(map[string]string)
+ }
+
+ // Pick a name. We try to use the final element of the path
+ // but fall back to _.
+ prefix = strings.TrimRight(url, "/")
+ if i := strings.LastIndex(prefix, "/"); i >= 0 {
+ prefix = prefix[i+1:]
+ }
+ if prefix == "" || !isName([]byte(prefix)) || strings.Contains(prefix, ":") {
+ prefix = "_"
+ }
+ if strings.HasPrefix(prefix, "xml") {
+ // xmlanything is reserved.
+ prefix = "_" + prefix
+ }
+ if p.attrNS[prefix] != "" {
+ // Name is taken. Find a better one.
+ for p.seq++; ; p.seq++ {
+ if id := prefix + "_" + strconv.Itoa(p.seq); p.attrNS[id] == "" {
+ prefix = id
+ break
+ }
+ }
+ }
+
+ p.attrPrefix[url] = prefix
+ p.attrNS[prefix] = url
+
+ p.WriteString(`xmlns:`)
+ p.WriteString(prefix)
+ p.WriteString(`="`)
+ EscapeText(p, []byte(url))
+ p.WriteString(`" `)
+
+ return prefix, true
+}
+
+// deleteAttrPrefix removes an attribute name space prefix.
+func (p *printer) deleteAttrPrefix(prefix string) {
+ delete(p.attrPrefix, p.attrNS[prefix])
+ delete(p.attrNS, prefix)
}
// marshalValue writes one or more XML elements representing val.
@@ -210,6 +275,14 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
continue
}
p.WriteByte(' ')
+ if finfo.xmlns != "" {
+ prefix, created := p.createAttrPrefix(finfo.xmlns)
+ if created {
+ defer p.deleteAttrPrefix(prefix)
+ }
+ p.WriteString(prefix)
+ p.WriteByte(':')
+ }
p.WriteString(finfo.name)
p.WriteString(`="`)
if err := p.marshalSimple(fv.Type(), fv); err != nil {
@@ -286,7 +359,7 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
s := parentStack{printer: p}
for i := range tinfo.fields {
finfo := &tinfo.fields[i]
- if finfo.flags&(fAttr) != 0 {
+ if finfo.flags&fAttr != 0 {
continue
}
vf := finfo.value(val)