diff options
Diffstat (limited to 'src/pkg/encoding/xml/marshal.go')
-rw-r--r-- | src/pkg/encoding/xml/marshal.go | 75 |
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) |