aboutsummaryrefslogtreecommitdiff
path: root/src/encoding/xml/marshal.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/encoding/xml/marshal.go')
-rw-r--r--src/encoding/xml/marshal.go162
1 files changed, 86 insertions, 76 deletions
diff --git a/src/encoding/xml/marshal.go b/src/encoding/xml/marshal.go
index 63f8e2aa87..100e41df24 100644
--- a/src/encoding/xml/marshal.go
+++ b/src/encoding/xml/marshal.go
@@ -578,12 +578,14 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
// 3. type name
var start StartElement
- // Historic behaviour: elements use the default name space
- // they are contained in by default.
- start.Name.Space = p.defaultNS
+ // explicitNS records whether the element's name
+ // space has been explicitly set (for example an
+ // and XMLName field).
+ explicitNS := false
if startTemplate != nil {
start.Name = startTemplate.Name
+ explicitNS = true
start.Attr = append(start.Attr, startTemplate.Attr...)
} else if tinfo.xmlname != nil {
xmlname := tinfo.xmlname
@@ -592,11 +594,13 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
} else if v, ok := xmlname.value(val).Interface().(Name); ok && v.Local != "" {
start.Name = v
}
+ explicitNS = true
}
if start.Name.Local == "" && finfo != nil {
start.Name.Local = finfo.name
if finfo.xmlns != "" {
start.Name.Space = finfo.xmlns
+ explicitNS = true
}
}
if start.Name.Local == "" {
@@ -606,9 +610,12 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
}
start.Name.Local = name
}
- // Historic behaviour: an element that's in a namespace sets
- // the default namespace for all elements contained within it.
- start.setDefaultNamespace()
+
+ // defaultNS records the default name space as set by a xmlns="..."
+ // attribute. We don't set p.defaultNS because we want to let
+ // the attribute writing code (in p.defineNS) be solely responsible
+ // for maintaining that.
+ defaultNS := p.defaultNS
// Attributes
for i := range tinfo.fields {
@@ -616,81 +623,26 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
if finfo.flags&fAttr == 0 {
continue
}
- fv := finfo.value(val)
- name := Name{Space: finfo.xmlns, Local: finfo.name}
-
- if finfo.flags&fOmitEmpty != 0 && isEmptyValue(fv) {
- continue
- }
-
- if fv.Kind() == reflect.Interface && fv.IsNil() {
- continue
- }
-
- if fv.CanInterface() && fv.Type().Implements(marshalerAttrType) {
- attr, err := fv.Interface().(MarshalerAttr).MarshalXMLAttr(name)
- if err != nil {
- return err
- }
- if attr.Name.Local != "" {
- start.Attr = append(start.Attr, attr)
- }
- continue
- }
-
- if fv.CanAddr() {
- pv := fv.Addr()
- if pv.CanInterface() && pv.Type().Implements(marshalerAttrType) {
- attr, err := pv.Interface().(MarshalerAttr).MarshalXMLAttr(name)
- if err != nil {
- return err
- }
- if attr.Name.Local != "" {
- start.Attr = append(start.Attr, attr)
- }
- continue
- }
- }
-
- if fv.CanInterface() && fv.Type().Implements(textMarshalerType) {
- text, err := fv.Interface().(encoding.TextMarshaler).MarshalText()
- if err != nil {
- return err
- }
- start.Attr = append(start.Attr, Attr{name, string(text)})
- continue
- }
-
- if fv.CanAddr() {
- pv := fv.Addr()
- if pv.CanInterface() && pv.Type().Implements(textMarshalerType) {
- text, err := pv.Interface().(encoding.TextMarshaler).MarshalText()
- if err != nil {
- return err
- }
- start.Attr = append(start.Attr, Attr{name, string(text)})
- continue
- }
- }
-
- // Dereference or skip nil pointer, interface values.
- switch fv.Kind() {
- case reflect.Ptr, reflect.Interface:
- if fv.IsNil() {
- continue
- }
- fv = fv.Elem()
- }
-
- s, b, err := p.marshalSimple(fv.Type(), fv)
+ attr, add, err := p.fieldAttr(finfo, val)
if err != nil {
return err
}
- if b != nil {
- s = string(b)
+ if !add {
+ continue
+ }
+ start.Attr = append(start.Attr, attr)
+ if attr.Name.Space == "" && attr.Name.Local == "xmlns" {
+ defaultNS = attr.Value
}
- start.Attr = append(start.Attr, Attr{name, s})
}
+ if !explicitNS {
+ // Historic behavior: elements use the default name space
+ // they are contained in by default.
+ start.Name.Space = defaultNS
+ }
+ // Historic behaviour: an element that's in a namespace sets
+ // the default namespace for all elements contained within it.
+ start.setDefaultNamespace()
if err := p.writeStart(&start); err != nil {
return err
@@ -719,6 +671,64 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat
return p.cachedWriteError()
}
+// fieldAttr returns the attribute of the given field and
+// whether it should actually be added as an attribute;
+// val holds the value containing the field.
+func (p *printer) fieldAttr(finfo *fieldInfo, val reflect.Value) (Attr, bool, error) {
+ fv := finfo.value(val)
+ name := Name{Space: finfo.xmlns, Local: finfo.name}
+ if finfo.flags&fOmitEmpty != 0 && isEmptyValue(fv) {
+ return Attr{}, false, nil
+ }
+ if fv.Kind() == reflect.Interface && fv.IsNil() {
+ return Attr{}, false, nil
+ }
+ if fv.CanInterface() && fv.Type().Implements(marshalerAttrType) {
+ attr, err := fv.Interface().(MarshalerAttr).MarshalXMLAttr(name)
+ return attr, attr.Name.Local != "", err
+ }
+ if fv.CanAddr() {
+ pv := fv.Addr()
+ if pv.CanInterface() && pv.Type().Implements(marshalerAttrType) {
+ attr, err := pv.Interface().(MarshalerAttr).MarshalXMLAttr(name)
+ return attr, attr.Name.Local != "", err
+ }
+ }
+ if fv.CanInterface() && fv.Type().Implements(textMarshalerType) {
+ text, err := fv.Interface().(encoding.TextMarshaler).MarshalText()
+ if err != nil {
+ return Attr{}, false, err
+ }
+ return Attr{name, string(text)}, true, nil
+ }
+ if fv.CanAddr() {
+ pv := fv.Addr()
+ if pv.CanInterface() && pv.Type().Implements(textMarshalerType) {
+ text, err := pv.Interface().(encoding.TextMarshaler).MarshalText()
+ if err != nil {
+ return Attr{}, false, err
+ }
+ return Attr{name, string(text)}, true, nil
+ }
+ }
+ // Dereference or skip nil pointer, interface values.
+ switch fv.Kind() {
+ case reflect.Ptr, reflect.Interface:
+ if fv.IsNil() {
+ return Attr{}, false, nil
+ }
+ fv = fv.Elem()
+ }
+ s, b, err := p.marshalSimple(fv.Type(), fv)
+ if err != nil {
+ return Attr{}, false, err
+ }
+ if b != nil {
+ s = string(b)
+ }
+ return Attr{name, s}, true, nil
+}
+
// defaultStart returns the default start element to use,
// given the reflect type, field info, and start template.
func (p *printer) defaultStart(typ reflect.Type, finfo *fieldInfo, startTemplate *StartElement) StartElement {